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
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
This commit is contained in:
@@ -12,45 +12,11 @@ not for structure or style.
|
|||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
In priority order, and the order is the point: where two of them pull against each other, the earlier
|
The nine goals, in priority order, live in
|
||||||
one wins. They do not override the hard rules below.
|
[README.md](https://gitea.larvit.se/larvit/smpp-js/src/branch/main/README.md#goals) — they say where this library is heading, which an outside
|
||||||
|
reader judges it by. The README states the audience alongside them. Everything below cites a goal by
|
||||||
|
number.
|
||||||
|
|
||||||
1. **Correct on the wire.** SMPP 3.4 as SMSCs actually run it. Every other goal yields to this one;
|
|
||||||
the defect table below is what the alternative costs.
|
|
||||||
2. **Never give the application a wrong answer about what happened.** An outcome we cannot determine
|
|
||||||
is reported as undetermined rather than guessed; a report the peer marked as not final settles
|
|
||||||
nothing, so nothing the library concludes may rest on one; a request the peer may already have
|
|
||||||
taken is never re-sent on the library's own initiative; work the peer has no reason to send again
|
|
||||||
is not dropped.
|
|
||||||
3. **Strict in what we send, generous in what we read.** The library's own senders follow 3.4, and
|
|
||||||
the codec parses whatever arrives. Where the letter of the spec would discard traffic a real SMSC
|
|
||||||
sends, keep the traffic.
|
|
||||||
4. **A peer an operator never has to complain about.** No bind flooding, nothing a bind direction
|
|
||||||
forbids, no optional parameters to a peer that declared none, nothing held without a bound.
|
|
||||||
5. **The session layer is in here, and its defaults are what most applications should run.**
|
|
||||||
Keepalive, reconnect, the send window, reassembly and receipt correlation. What the network says
|
|
||||||
about a message the application sent reaches it as a report rather than as an inbound message, and
|
|
||||||
says whether it is final, so nothing has to read the PDU to tell those apart. An option retunes a
|
|
||||||
default or opts out of it; an option does not switch on the thing the caller obviously wanted.
|
|
||||||
6. **Configurable and extendable, never at the defaults' expense.** Where an application needs other
|
|
||||||
than the default and cannot build it from what is exported — a rate limit counted per PDU, an
|
|
||||||
alphabet, a receipt format — it gets an option or a hook rather than a fork. A call that passes no
|
|
||||||
options stays exactly as easy and as safe, and a hook is a seam the library calls, never a way into
|
|
||||||
its internals.
|
|
||||||
7. **A small, stable public surface over reshapeable internals.** Only what `src/index.ts` exports is
|
|
||||||
published. A new option has to beat "the application can do this itself", and has to keep a
|
|
||||||
promise this library can verify. The low-level surface is a passthrough: policy binds what the
|
|
||||||
library composes, never what the caller wrote.
|
|
||||||
8. **State wider than one session goes through one store.** A pool of sessions, a limit shared
|
|
||||||
between processes, and what has to survive a restart — receipts still awaited, a message half
|
|
||||||
reassembled — are held through a store interface and never beside it. Without a store the
|
|
||||||
application supplies, that state is in memory and ends with the process, and the defaults need
|
|
||||||
none. The interface carries the library's own versioned records, never an internal shape handed
|
|
||||||
to the application to persist. Coordinating processes any other way is declined without a fresh
|
|
||||||
argument each time.
|
|
||||||
9. **It builds, tests and runs the same everywhere.** Container-only toolchain, no runtime
|
|
||||||
dependencies, the Node 18 floor verified in CI rather than asserted, every README example executed
|
|
||||||
by the suite.
|
|
||||||
|
|
||||||
## Hard rules
|
## Hard rules
|
||||||
|
|
||||||
@@ -245,12 +211,13 @@ decision under [The wire](#the-wire).
|
|||||||
|
|
||||||
Each file answers one question, and a fact belongs to the file whose question it answers:
|
Each file answers one question, and a fact belongs to the file whose question it answers:
|
||||||
|
|
||||||
- **README.md — what you can rely on.** Observable behaviour, for someone using the package. It
|
- **README.md — what you can rely on, and where this is heading.** Observable behaviour, for
|
||||||
carries a reason only where the reason changes how you would call the thing.
|
someone using the package, plus the goals and the audience. It carries a reason only where the
|
||||||
|
reason changes how you would call the thing.
|
||||||
- **MIGRATION.md — what a 0.4.0 consumer has to change.** Renamed and removed surface, and the
|
- **MIGRATION.md — what a 0.4.0 consumer has to change.** Renamed and removed surface, and the
|
||||||
behaviour that changed on the wire.
|
behaviour that changed on the wire.
|
||||||
- **AGENTS.md — what may not change, and why.** Goals, hard rules, architecture, conventions, and the
|
- **AGENTS.md — what may not change, and why.** Hard rules, architecture, conventions, and the
|
||||||
decisions the goals do not already settle. It does not restate behaviour README states.
|
decisions the goals do not already settle. It does not restate behaviour or goals README states.
|
||||||
- **todo.md** is a working file that sets its own rules; nothing here governs it.
|
- **todo.md** is a working file that sets its own rules; nothing here governs it.
|
||||||
|
|
||||||
A sentence living in two of them is a defect: delete the copy in the file whose question it does not
|
A sentence living in two of them is a defect: delete the copy in the file whose question it does not
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ window, long messages and delivery receipts. TypeScript, ESM, no dependencies.
|
|||||||
[Send options](#send-options) · [Session](#session) · [Receiving in depth](#receiving-in-depth) ·
|
[Send options](#send-options) · [Session](#session) · [Receiving in depth](#receiving-in-depth) ·
|
||||||
[Server in depth](#server-in-depth) · [Logging](#logging) ·
|
[Server in depth](#server-in-depth) · [Logging](#logging) ·
|
||||||
[PDUs and the low-level API](#pdus-and-the-low-level-api) ·
|
[PDUs and the low-level API](#pdus-and-the-low-level-api) ·
|
||||||
[Migrating from 0.4.0](#migrating-from-larvitsmpp-040) · [Development](#development)
|
[Migrating from 0.4.0](#migrating-from-larvitsmpp-040) · [Goals](#goals) ·
|
||||||
|
[Audience](#audience) · [Development](#development)
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
@@ -632,6 +633,72 @@ if (isCommand(pduObj, 'submit_sm')) {
|
|||||||
|
|
||||||
See [MIGRATION.md](https://gitea.larvit.se/larvit/smpp-js/src/branch/main/MIGRATION.md).
|
See [MIGRATION.md](https://gitea.larvit.se/larvit/smpp-js/src/branch/main/MIGRATION.md).
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
In priority order, and the order is the point: where two of them pull against each other, the earlier
|
||||||
|
one wins. They do not override the hard rules below.
|
||||||
|
|
||||||
|
1. **Correct on the wire.** SMPP 3.4 as SMSCs actually run it. Every other goal yields to this one;
|
||||||
|
the defect table below is what the alternative costs.
|
||||||
|
2. **Never give the application a wrong answer about what happened.** An outcome we cannot determine
|
||||||
|
is reported as undetermined rather than guessed; a report the peer marked as not final settles
|
||||||
|
nothing, so nothing the library concludes may rest on one; a request the peer may already have
|
||||||
|
taken is never re-sent on the library's own initiative; work the peer has no reason to send again
|
||||||
|
is not dropped.
|
||||||
|
3. **Strict in what we send, generous in what we read.** The library's own senders follow 3.4, and
|
||||||
|
the codec parses whatever arrives. Where the letter of the spec would discard traffic a real SMSC
|
||||||
|
sends, keep the traffic.
|
||||||
|
4. **A peer an operator never has to complain about.** No bind flooding, nothing a bind direction
|
||||||
|
forbids, no optional parameters to a peer that declared none, nothing held without a bound.
|
||||||
|
5. **The session layer is in here, and its defaults are what most applications should run.**
|
||||||
|
Keepalive, reconnect, the send window, reassembly and receipt correlation. What the network says
|
||||||
|
about a message the application sent reaches it as a report rather than as an inbound message, and
|
||||||
|
says whether it is final, so nothing has to read the PDU to tell those apart. An option retunes a
|
||||||
|
default or opts out of it; an option does not switch on the thing the caller obviously wanted.
|
||||||
|
6. **Configurable and extendable, never at the defaults' expense.** Where an application needs other
|
||||||
|
than the default and cannot build it from what is exported — a rate limit counted per PDU, an
|
||||||
|
alphabet, a receipt format — it gets an option or a hook rather than a fork. A call that passes no
|
||||||
|
options stays exactly as easy and as safe, and a hook is a seam the library calls, never a way into
|
||||||
|
its internals.
|
||||||
|
7. **A small, stable public surface over reshapeable internals.** Only what `src/index.ts` exports is
|
||||||
|
published. A new option has to beat "the application can do this itself", and has to keep a
|
||||||
|
promise this library can verify. The low-level surface is a passthrough: policy binds what the
|
||||||
|
library composes, never what the caller wrote.
|
||||||
|
8. **State wider than one session goes through one store.** A pool of sessions, a limit shared
|
||||||
|
between processes, and what has to survive a restart — receipts still awaited, a message half
|
||||||
|
reassembled — are held through a store interface and never beside it. Without a store the
|
||||||
|
application supplies, that state is in memory and ends with the process, and the defaults need
|
||||||
|
none. The interface carries the library's own versioned records, never an internal shape handed
|
||||||
|
to the application to persist. Coordinating processes any other way is declined without a fresh
|
||||||
|
argument each time.
|
||||||
|
9. **It builds, tests and runs the same everywhere.** Container-only toolchain, no runtime
|
||||||
|
dependencies, the Node 18 floor verified in CI rather than asserted, every README example executed
|
||||||
|
by the suite.
|
||||||
|
|
||||||
|
## Audience
|
||||||
|
|
||||||
|
Who depends on this library, and what they may rely on.
|
||||||
|
|
||||||
|
- **The public npm audience, not only larvit's own applications.** Only what `src/index.ts` exports
|
||||||
|
is public; everything behind it is reshaped freely.
|
||||||
|
- **Node 18 and newer, ESM only, no runtime dependencies.** The floor is verified in CI rather than
|
||||||
|
asserted, so the library drops into a service or a container without pulling a tree behind it.
|
||||||
|
- **Real SMSCs and ESMEs as operators actually run them**, not a reference implementation. Jasmin,
|
||||||
|
SMPPSim, Kannel, jsmpp, Cloudhopper, python-smpplib and php-smpp are the interop targets, and what
|
||||||
|
they do in practice outranks what the specification says they should do.
|
||||||
|
- **The SMSC operator on the far end**, who never sees this API but carries what it does to their
|
||||||
|
link. A peer they have to complain about is a defect however well the library reads.
|
||||||
|
- **Pre-1.0, so the minor is the breaking unit** and a patch never breaks. What a 0.4.0 consumer has
|
||||||
|
to change is in [MIGRATION.md](https://gitea.larvit.se/larvit/smpp-js/src/branch/main/MIGRATION.md).
|
||||||
|
|
||||||
|
Personas this README serves, in order:
|
||||||
|
|
||||||
|
1. The application developer sending or receiving SMS on the defaults, who should need no options.
|
||||||
|
2. The application developer who needs one thing retuned — an alphabet, a rate limit, a receipt
|
||||||
|
format — through an option or a hook rather than a fork.
|
||||||
|
3. The operator coordinating sessions across processes through a store.
|
||||||
|
4. The developer migrating from `larvitsmpp` 0.4.0.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Everything runs in the container; nothing is installed on the host.
|
Everything runs in the container; nothing is installed on the host.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# todo.md
|
# todo.md
|
||||||
|
|
||||||
Remaining work for `@larvit/smpp`. Read [AGENTS.md](AGENTS.md) first — the goals and hard rules
|
Remaining work for `@larvit/smpp`. Read [README.md](README.md)'s goals and [AGENTS.md](AGENTS.md)'s
|
||||||
there constrain every item below.
|
hard rules first — they constrain every item below.
|
||||||
|
|
||||||
This is a working file that sets its own rules. The documentation conventions in AGENTS.md do not
|
This is a working file that sets its own rules. The documentation conventions in AGENTS.md do not
|
||||||
govern it, and nothing here is a source anything else may cite.
|
govern it, and nothing here is a source anything else may cite.
|
||||||
@@ -262,7 +262,7 @@ the rewrite, for a dependency added later. Maintainer's call, 2026-09-14.
|
|||||||
|
|
||||||
From comparing 0.5.0 with `smpp`, `@semyonf/smpp`, `@leissner/node-red-smpp`, `node-smpp-next`,
|
From comparing 0.5.0 with `smpp`, `@semyonf/smpp`, `@leissner/node-red-smpp`, `node-smpp-next`,
|
||||||
`smpp-js-sdk`, `smppjs`, cloudhopper-smpp, jsmpp, go-smpp, Kannel, Jasmin and php-smpp, 2026-09-14.
|
`smpp-js-sdk`, `smppjs`, cloudhopper-smpp, jsmpp, go-smpp, Kannel, Jasmin and php-smpp, 2026-09-14.
|
||||||
Each lands under AGENTS.md goal 6: an option or a hook, with the call that passes none unchanged.
|
Each lands under goal 6: an option or a hook, with the call that passes none unchanged.
|
||||||
|
|
||||||
### Sending
|
### Sending
|
||||||
|
|
||||||
@@ -424,7 +424,7 @@ Each lands under AGENTS.md goal 6: an option or a hook, with the call that passe
|
|||||||
|
|
||||||
- [ ] **Pooling, and state that survives a restart, through an optional store.** Maintainer's call,
|
- [ ] **Pooling, and state that survives a restart, through an optional store.** Maintainer's call,
|
||||||
2026-09-14. It replaces two declines — merge state surviving a restart, and a pool of sessions —
|
2026-09-14. It replaces two declines — merge state surviving a restart, and a pool of sessions —
|
||||||
and AGENTS.md goal 8 was rewritten for it. Big: design before code.
|
and goal 8 was rewritten for it. Big: design before code.
|
||||||
- **What it holds.** Receipts still awaited and the groups `DlrMerger` collects. Segments of a
|
- **What it holds.** Receipts still awaited and the groups `DlrMerger` collects. Segments of a
|
||||||
message already answered but not yet whole, which the peer will not send again (goal 2). The
|
message already answered but not yet whole, which the peer will not send again (goal 2). The
|
||||||
concatenation reference, so a restart does not reuse one. For a pool, the ids every session
|
concatenation reference, so a restart does not reuse one. For a pool, the ids every session
|
||||||
|
|||||||
Reference in New Issue
Block a user