Initial Commit
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# drive-transport-https spec — v1
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Implements TransportLike<MessageEnvelope>
|
||||
|
||||
`HttpsTransport` SHALL implement the structural
|
||||
`TransportLike<MessageEnvelope>` contract from
|
||||
`@suaveplan/types/transport`: `connect(peerId, address)`,
|
||||
`disconnect(peerId)`, `send(peerId, envelope)`,
|
||||
`onMessage(handler) → Unsubscribe`, `getConnectedPeers()`. It SHALL
|
||||
add lifecycle methods `start()` / `stop()` so the long-poll loop
|
||||
does not begin until the consumer is ready and so the loop can be
|
||||
cleanly torn down on shutdown.
|
||||
|
||||
**Verified by:** `tests/lifecycle.test.ts` (eleven scenarios).
|
||||
|
||||
### Requirement: Long-poll receive with cursor
|
||||
|
||||
The transport SHALL maintain a string `cursor` initialised to `""`.
|
||||
Every long-poll request SHALL include the cursor as the `since`
|
||||
query parameter when non-empty; on response, the cursor SHALL be
|
||||
replaced with the `nextCursor` field from the parsed
|
||||
`RecvResponse`.
|
||||
|
||||
**Verified by:** `tests/recv.test.ts`::"includes since=cursor on
|
||||
subsequent /recv calls" AND "cursor starts empty and updates after
|
||||
a successful /recv" (`tests/lifecycle.test.ts`).
|
||||
|
||||
### Requirement: Auth-failure short-circuit
|
||||
|
||||
A `401` or `403` response from `/recv` SHALL terminate the receive
|
||||
loop. The transport's `state` SHALL transition to `"stopped"` and
|
||||
the `lastAuthError` getter SHALL expose the typed
|
||||
`HttpsTransportError(HTTPS_TRANSPORT.AUTH_FAILED, ...)`. The
|
||||
transport SHALL NOT auto-retry on auth failure — credential refresh
|
||||
belongs to the consumer.
|
||||
|
||||
**Verified by:** `tests/recv.test.ts`::"AUTH_FAILED on /recv stops
|
||||
the loop" AND "AUTH_FAILED on /recv propagates via lastAuthError".
|
||||
|
||||
### Requirement: Exponential backoff with equal jitter
|
||||
|
||||
On a transient receive failure (`5xx` /
|
||||
`HTTPS_TRANSPORT.RECV_FAILED` /
|
||||
`HTTPS_TRANSPORT.MALFORMED_RESPONSE`), the loop SHALL back off and
|
||||
retry. Wait times SHALL follow an equal-jitter formula
|
||||
`exp / 2 + rand(0, exp/2)` clamped to `[backoffBaseMs,
|
||||
backoffMaxMs]` with `exp = backoffBaseMs * 2^(failures-1)`.
|
||||
|
||||
**Verified by:** `tests/recv.test.ts`::"backs off on 503 and
|
||||
retries", "backs off on 4xx other than auth (RECV_FAILED) and
|
||||
retries", "backs off on malformed JSON body (MALFORMED_RESPONSE)
|
||||
and retries".
|
||||
|
||||
### Requirement: Send wire contract
|
||||
|
||||
`send(peerId, envelope)` SHALL `POST` to `${relayUrl}/send` with
|
||||
`{ to, envelope }` as JSON. The peer SHALL be in the `connected`
|
||||
set or `HTTPS_TRANSPORT.PEER_UNKNOWN` is thrown. `401`/`403`
|
||||
responses SHALL throw `AUTH_FAILED`; `5xx` responses SHALL throw
|
||||
`RELAY_UNAVAILABLE`; other non-2xx responses SHALL throw
|
||||
`SEND_FAILED`. `200` responses SHALL be Zod-validated against
|
||||
`SendResponseSchema`; `ok: false` SHALL throw `SEND_FAILED` with
|
||||
the relay's `code` (or `"unknown"`) in the message.
|
||||
|
||||
**Verified by:** `tests/send.test.ts` (fifteen scenarios).
|
||||
|
||||
### Requirement: Lifecycle invariants
|
||||
|
||||
`start()` SHALL throw `HTTPS_TRANSPORT.CLOSED` if called after
|
||||
`stop()`. `start()` SHALL be idempotent while `state === "running"`.
|
||||
`stop()` SHALL be idempotent. `connect()` and `send()` SHALL throw
|
||||
`HTTPS_TRANSPORT.NOT_CONNECTED` if the transport has not been
|
||||
started, and `HTTPS_TRANSPORT.CLOSED` if it has been stopped.
|
||||
|
||||
**Verified by:** `tests/lifecycle.test.ts` and `tests/send.test.ts`.
|
||||
Reference in New Issue
Block a user