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
|
# AGENTS.md
|
||||||
|
|
||||||
Guidance for LLM agents working in this repository. What each file in it is for is under
|
|
||||||
[Documentation](#documentation).
|
|
||||||
|
|
||||||
## What this is
|
## What this is
|
||||||
|
|
||||||
A ground-up TypeScript rewrite of `larvitsmpp` 0.4.0, published as `@larvit/smpp` 0.5.0. The branch
|
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
|
## Goals
|
||||||
|
|
||||||
The goals, in priority order, live in
|
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
|
## Hard rules
|
||||||
|
|
||||||
@@ -87,8 +84,8 @@ src/
|
|||||||
|
|
||||||
Imports point one way: `defs` knows nothing above it but `result.ts`, `pdu` uses `defs`, `session`
|
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
|
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`
|
`createSms()`, `HeldMessages` and `IncomingRequests`, which call back into it, and to `OnRequest`
|
||||||
in `session-options.ts`, all imported as a type only.
|
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
|
**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
|
written to and read from the buffer. Never sort those alphabetically — the alphabetical-ordering
|
||||||
@@ -225,10 +222,6 @@ this is not a changelog.
|
|||||||
|
|
||||||
## Decisions
|
## 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)
|
### [The public surface](docs/decisions.md#the-public-surface)
|
||||||
|
|
||||||
- `Session` is publicly constructible, which is what makes `SessionOptions` and `ReconnectOptions`
|
- `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
|
`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.
|
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.
|
- `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
|
- The `SmsInput` type is no longer exported; nothing exported took one. Annotate with `Sms`, or a
|
||||||
`Pick<Sms, 'from' | 'message' | 'to'>` where you typed a shape of your own.
|
`Pick<Sms, …>` of the fields you use.
|
||||||
- `session.boundAs` and `session.peerInterfaceVersion` are read-only, and `session.loggedIn` is
|
- `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
|
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
|
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
|
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
|
options stays exactly as easy and as safe, and a hook is a seam the library calls, never a way into
|
||||||
its internals.
|
its internals.
|
||||||
8. **A small, stable public surface over reshapeable internals.** Only what `src/index.ts` exports is
|
8. **A small, stable public surface over reshapeable internals.** A new option has to beat "the
|
||||||
published. A new option has to beat "the application can do this itself", and has to keep a
|
application can do this itself", and has to keep a promise this library can verify. The low-level
|
||||||
promise this library can verify. The low-level surface is a passthrough: policy binds what the
|
surface is a passthrough: policy binds what the library composes, never what the caller wrote.
|
||||||
library composes, never what the caller wrote.
|
|
||||||
9. **State wider than one session goes through one store.** A pool of sessions, a limit shared
|
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
|
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
|
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`
|
- **`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
|
public too.** Raised twice as a leak; it is not one. The collaborators `session.ts` delegates to
|
||||||
(`IncomingRequests`, `OutgoingRequests`, `ReconnectLoop`, `LinkTimers`, `DlrMerger`,
|
stay unpublished so they can be reshaped.
|
||||||
`PduTransport`, `submitSms`) stay unpublished so they can be reshaped.
|
|
||||||
|
|
||||||
- **`acceptsOptionalParams()` and `bindAllows()` are predicates, not chokepoints.** The library's own
|
- **`acceptsOptionalParams()` and `bindAllows()` are predicates, not chokepoints.** The library's own
|
||||||
senders consult them; `session.send({ tlvs })` is passed through as written, because silently
|
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
|
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
|
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
|
architecture review of [#99](https://github.com/larvit/larvitsmpp/pull/99), 2026-09-09. It is
|
||||||
reached through
|
reached through `encodeBody()` in `message.ts`, which is where the `data_coding`-to-text pair already lives:
|
||||||
`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
|
`encodeBody(text, dataCoding)` is `decodeMessage(buffer, dataCoding)`'s mirror and resolves the
|
||||||
alphabet through the same `encodingByDataCoding()`. `send()` and `sendReturn()` inherit it,
|
alphabet through the same `encodingByDataCoding()`. `send()` and `sendReturn()` inherit it,
|
||||||
since both build through `buildPdu()`; `sendSms()` does not, and keeps its own guard, because
|
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.
|
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
|
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`.
|
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
|
Nothing is held for a request the hook answered, so the drain waits on none of it. `OnRequest` stays unexported where
|
||||||
hook skipped, 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
|
`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
|
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
|
bind and authentication inside the application's reach for nothing. Rejected: a narrower hook
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ export type HeldMessagesOptions = {
|
|||||||
maxOctets: number;
|
maxOctets: number;
|
||||||
/** Injected so expiry can be exercised without a wall clock. */
|
/** Injected so expiry can be exercised without a wall clock. */
|
||||||
now?: (() => number) | undefined;
|
now?: (() => number) | undefined;
|
||||||
/** Past a drain's refusal, for a receipt the drain is itself waiting for. */
|
|
||||||
sendPastDrain: SmsHandlers['send'];
|
sendPastDrain: SmsHandlers['send'];
|
||||||
session: Session;
|
session: Session;
|
||||||
timeout: number;
|
timeout: number;
|
||||||
@@ -135,7 +134,6 @@ export class HeldMessages {
|
|||||||
return hold;
|
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 {
|
offer(pduObjs: PduObject[], answeredAs?: string): MessageHold | undefined {
|
||||||
const key = keyOf(pduObjs);
|
const key = keyOf(pduObjs);
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ export type SmsInput = {
|
|||||||
session: Session;
|
session: Session;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** What the session's incoming side gives a message so it can be answered and accounted for. */
|
|
||||||
export type SmsHandlers = {
|
export type SmsHandlers = {
|
||||||
answered: () => void;
|
answered: () => void;
|
||||||
lostLink: () => boolean;
|
lostLink: () => boolean;
|
||||||
|
|||||||
@@ -352,6 +352,12 @@ below.
|
|||||||
|
|
||||||
### Self-sufficiency — 6–7 today, and the gate is 7
|
### 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
|
- [ ] **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,
|
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
|
with no link from the code; one counted roughly fifty index redirects. The three left to inline
|
||||||
|
|||||||
Reference in New Issue
Block a user