Cut the prose this change made false or redundant
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 33s
Test / test (24) (pull_request) Successful in 35s
Test / test (26) (pull_request) Successful in 33s
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 33s
Test / test (24) (pull_request) Successful in 35s
Test / test (26) (pull_request) Successful in 33s
This commit is contained in:
@@ -1,8 +1,5 @@
|
||||
# AGENTS.md
|
||||
|
||||
Guidance for LLM agents working in this repository. What each file in it is for is under
|
||||
[Documentation](#documentation).
|
||||
|
||||
## What this is
|
||||
|
||||
A ground-up TypeScript rewrite of `larvitsmpp` 0.4.0, published as `@larvit/smpp` 0.5.0. The branch
|
||||
@@ -13,7 +10,7 @@ not for structure or style.
|
||||
## Goals
|
||||
|
||||
The goals, in priority order, live in
|
||||
[README.md](https://gitea.larvit.se/larvit/smpp-js/src/branch/main/README.md#goals). The README states the audience alongside them.
|
||||
[README.md](https://gitea.larvit.se/larvit/smpp-js/src/branch/main/README.md#goals).
|
||||
|
||||
## Hard rules
|
||||
|
||||
@@ -87,8 +84,8 @@ src/
|
||||
|
||||
Imports point one way: `defs` knows nothing above it but `result.ts`, `pdu` uses `defs`, `session`
|
||||
uses `pdu`, and `client`/`server` use `session`. The ways back up are the `Session` handed to
|
||||
`createSms()`, `HeldMessages` and `IncomingRequests`, which call back into it, and to `OnRequest` and `onConnected`
|
||||
in `session-options.ts`, all imported as a type only.
|
||||
`createSms()`, `HeldMessages` and `IncomingRequests`, which call back into it, and to `OnRequest`
|
||||
and `onConnected` in `session-options.ts`, all imported as a type only.
|
||||
|
||||
**Parameter order is wire order.** The key order inside `cmds.*.params` is the order the fields are
|
||||
written to and read from the buffer. Never sort those alphabetically — the alphabetical-ordering
|
||||
@@ -225,10 +222,6 @@ this is not a changelog.
|
||||
|
||||
## Decisions
|
||||
|
||||
The decisions themselves live in [docs/decisions.md](docs/decisions.md). Their titles are indexed
|
||||
here, so a reader sees that a decision exists without carrying its reasoning; the reasoning is in
|
||||
the file.
|
||||
|
||||
### [The public surface](docs/decisions.md#the-public-surface)
|
||||
|
||||
- `Session` is publicly constructible, which is what makes `SessionOptions` and `ReconnectOptions`
|
||||
|
||||
+2
-2
@@ -74,8 +74,8 @@
|
||||
`alert_on_message_delivery` and `broadcast_area_identifier`, the names they read back under. The
|
||||
two alternate names are gone from `tlvs` too, which is now typed by `TlvName`: narrow a `string` with `isTlvName()` before indexing it.
|
||||
- `cmds.broadcast_sm_resp.tlvMap` is removed; nothing read it.
|
||||
- The `SmsInput` type is no longer exported; nothing exported took one. Annotate with `Sms`, or
|
||||
`Pick<Sms, 'from' | 'message' | 'to'>` where you typed a shape of your own.
|
||||
- The `SmsInput` type is no longer exported; nothing exported took one. Annotate with `Sms`, or a
|
||||
`Pick<Sms, …>` of the fields you use.
|
||||
- `session.boundAs` and `session.peerInterfaceVersion` are read-only, and `session.loggedIn` is
|
||||
removed: read `session.boundAs !== undefined`. A session you wire yourself records the bind it
|
||||
accepted or had accepted with `session.bound(bindType, declaredVersion)`, which returns `err` for a
|
||||
|
||||
@@ -713,10 +713,9 @@ one wins. They do not override the [hard rules](https://gitea.larvit.se/larvit/s
|
||||
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.
|
||||
8. **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. **A small, stable public surface over reshapeable internals.** 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.
|
||||
9. **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
|
||||
|
||||
+3
-6
@@ -8,8 +8,7 @@ rule and an index of the titles below.
|
||||
|
||||
- **`Session` is publicly constructible, which is what makes `SessionOptions` and `ReconnectOptions`
|
||||
public too.** Raised twice as a leak; it is not one. The collaborators `session.ts` delegates to
|
||||
(`IncomingRequests`, `OutgoingRequests`, `ReconnectLoop`, `LinkTimers`, `DlrMerger`,
|
||||
`PduTransport`, `submitSms`) stay unpublished so they can be reshaped.
|
||||
stay unpublished so they can be reshaped.
|
||||
|
||||
- **`acceptsOptionalParams()` and `bindAllows()` are predicates, not chokepoints.** The library's own
|
||||
senders consult them; `session.send({ tlvs })` is passed through as written, because silently
|
||||
@@ -418,8 +417,7 @@ rule and an index of the titles below.
|
||||
caller would get wrong, where asking the codec is, and publishing it would freeze this library's
|
||||
error prose as API for an application whose own refusal should read like itself. Goal 8, from the
|
||||
architecture review of [#99](https://github.com/larvit/larvitsmpp/pull/99), 2026-09-09. It is
|
||||
reached through
|
||||
`encodeBody()` in `message.ts`, which is where the `data_coding`-to-text pair already lives:
|
||||
reached through `encodeBody()` in `message.ts`, which is where the `data_coding`-to-text pair already lives:
|
||||
`encodeBody(text, dataCoding)` is `decodeMessage(buffer, dataCoding)`'s mirror and resolves the
|
||||
alphabet through the same `encodingByDataCoding()`. `send()` and `sendReturn()` inherit it,
|
||||
since both build through `buildPdu()`; `sendSms()` does not, and keeps its own guard, because
|
||||
@@ -641,8 +639,7 @@ rule and an index of the titles below.
|
||||
failure belongs to one session's request, and that channel already carries every failure of one.
|
||||
The hook is consulted before the bind-direction gate, so it sees a `submit_sm` a receiver-bound
|
||||
peer may not send; first refusal means first, and one it declines still gets `ESME_RINVBNDSTS`.
|
||||
Nothing is held for a request the hook answered: `HeldMessages` is opened by the `sms` event the
|
||||
hook skipped, so the drain waits on none of it. `OnRequest` stays unexported where
|
||||
Nothing is held for a request the hook answered, so the drain waits on none of it. `OnRequest` stays unexported where
|
||||
`AuthenticateInput` is exported, because that hook's argument is a shape this library invents and
|
||||
this one's are two types already published. Rejected: consulting the hook first, which puts
|
||||
bind and authentication inside the application's reach for nothing. Rejected: a narrower hook
|
||||
|
||||
@@ -16,7 +16,6 @@ export type HeldMessagesOptions = {
|
||||
maxOctets: number;
|
||||
/** Injected so expiry can be exercised without a wall clock. */
|
||||
now?: (() => number) | undefined;
|
||||
/** Past a drain's refusal, for a receipt the drain is itself waiting for. */
|
||||
sendPastDrain: SmsHandlers['send'];
|
||||
session: Session;
|
||||
timeout: number;
|
||||
@@ -135,7 +134,6 @@ export class HeldMessages {
|
||||
return hold;
|
||||
}
|
||||
|
||||
/** Hands a whole message to the application as an `sms` event, held until it is answered. */
|
||||
offer(pduObjs: PduObject[], answeredAs?: string): MessageHold | undefined {
|
||||
const key = keyOf(pduObjs);
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@ export type SmsInput = {
|
||||
session: Session;
|
||||
};
|
||||
|
||||
/** What the session's incoming side gives a message so it can be answered and accounted for. */
|
||||
export type SmsHandlers = {
|
||||
answered: () => void;
|
||||
lostLink: () => boolean;
|
||||
|
||||
@@ -352,6 +352,12 @@ below.
|
||||
|
||||
### Self-sufficiency — 6–7 today, and the gate is 7
|
||||
|
||||
- [ ] **Move the fixture-copy reasoning out of AGENTS.md's Conventions, and split the longest
|
||||
decision entries.** The fixtures bullet holds four justifications for tolerated copies — decisions,
|
||||
so they belong in `docs/decisions.md` under Internals and tests; the entries under the wire's
|
||||
alphabet and body rules run 30–40 lines with their `Rejected:` clauses inline, where a reader
|
||||
who knows the answer still hunts for it. From the 2026-09-29 prose pass.
|
||||
|
||||
- [ ] **Move the one-line facts out of the decision log and back to the code.** Five of nine readers
|
||||
independently reported being sent to `docs/decisions.md` for a question they hit while reading,
|
||||
with no link from the code; one counted roughly fifty index redirects. The three left to inline
|
||||
|
||||
Reference in New Issue
Block a user