Initial Commit
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# drive-transport-https-v1
|
||||
|
||||
## Why
|
||||
|
||||
`@suaveplan/drive-mesh-protocol` defines the wire format for chunk
|
||||
discovery / request / delivery envelopes. To put those envelopes on
|
||||
the wire, drive-mesh consumers need a `TransportLike<MessageEnvelope>`
|
||||
implementation. This change ships the first one — an HTTPS-relay
|
||||
backed transport that works in browsers, sits behind any TLS
|
||||
proxy, and only requires the relay to host an HTTP server.
|
||||
|
||||
The HTTPS path is the lowest-friction connectivity tier in the drive
|
||||
stack: every browser, every server, every CI runner can speak it.
|
||||
WebRTC and WebSocket transports follow.
|
||||
|
||||
## What Changes
|
||||
|
||||
- New package `@suaveplan/drive-transport-https` at
|
||||
`packages/drive/drive-transport-https/` with full Vite + Vitest +
|
||||
Biome scaffolding.
|
||||
- `HttpsTransport` class implementing
|
||||
`TransportLike<MessageEnvelope>` from `@suaveplan/types/transport`
|
||||
with explicit `start()` / `stop()` lifecycle.
|
||||
- Long-poll receive loop (`GET /recv?for=...&since=cursor&timeoutMs=...`)
|
||||
with exponential backoff, equal jitter, AbortSignal cancellation,
|
||||
and a `lastAuthError` diagnostic getter for credential-refresh
|
||||
flows.
|
||||
- Stateless send (`POST /send` with `{ to, envelope }` body) with
|
||||
Zod-validated request and response shapes.
|
||||
- Nine frozen `HTTPS_TRANSPORT_CODES` (`NOT_CONNECTED` / `CLOSED` /
|
||||
`SEND_FAILED` / `RECV_FAILED` / `AUTH_FAILED` /
|
||||
`RELAY_UNAVAILABLE` / `MALFORMED_RESPONSE` / `ABORTED` /
|
||||
`PEER_UNKNOWN`).
|
||||
- Auth-failure short-circuit: a `401`/`403` from `/recv` stops the
|
||||
loop instead of looping forever — back-pressure belongs to the
|
||||
consumer.
|
||||
- 58 tests across 6 files at 100/100/100/100 coverage; 5/5 docs
|
||||
coverage.
|
||||
|
||||
## Impact
|
||||
|
||||
- New public package. No consumers yet — drive-mesh wiring will
|
||||
follow.
|
||||
- Browser-friendly: no Node-only imports. Works against any HTTP
|
||||
relay that implements the documented `/send` and `/recv`
|
||||
endpoints.
|
||||
@@ -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`.
|
||||
@@ -0,0 +1,50 @@
|
||||
# tasks — drive-transport-https-v1
|
||||
|
||||
## 1. Package scaffolding
|
||||
|
||||
- [x] 1.1 `package.json` with subpath exports for root, types,
|
||||
schemas, errors.
|
||||
- [x] 1.2 `vite.config.ts` with the standard externals list.
|
||||
- [x] 1.3 `tsconfig.lib.json` extending the base config.
|
||||
- [x] 1.4 `vitest.config.ts` via `createTestConfig()`.
|
||||
|
||||
## 2. Error registry
|
||||
|
||||
- [x] 2.1 Define the nine `HTTPS_TRANSPORT_CODES` and `HttpsTransportError`.
|
||||
- [x] 2.2 Co-located `error-codes.md` (≥200-word doc).
|
||||
|
||||
## 3. Public types
|
||||
|
||||
- [x] 3.1 `HttpsTransportOptions`, `FetchFn`, `RecvResponse`,
|
||||
`SendBody`, `SendResponse`, `Unsubscribe` in `types/transport.types.ts`.
|
||||
- [x] 3.2 Co-located `transport.types.md`.
|
||||
|
||||
## 4. Boundary schemas
|
||||
|
||||
- [x] 4.1 Zod schemas: `HttpsTransportOptionsSchema`,
|
||||
`SendBodySchema`, `SendResponseSchema`, `RecvResponseSchema`.
|
||||
- [x] 4.2 Co-located `transport.schema.md`.
|
||||
|
||||
## 5. Transport runtime
|
||||
|
||||
- [x] 5.1 `HttpsTransport` class implementing `TransportLike`.
|
||||
- [x] 5.2 Long-poll receive loop with backoff + jitter.
|
||||
- [x] 5.3 `lastAuthError` diagnostic getter for the auth-fail
|
||||
termination path.
|
||||
- [x] 5.4 `defaults.ts` constants module.
|
||||
- [x] 5.5 Co-located `https-transport.md` and `defaults.md`.
|
||||
|
||||
## 6. Tests
|
||||
|
||||
- [x] 6.1 `error-codes.spec-conformance.test.ts`.
|
||||
- [x] 6.2 `schemas.test.ts`.
|
||||
- [x] 6.3 `lifecycle.test.ts`.
|
||||
- [x] 6.4 `send.test.ts`.
|
||||
- [x] 6.5 `recv.test.ts`.
|
||||
- [x] 6.6 `default-fetch.test.ts` (isolated globalThis.fetch tests).
|
||||
|
||||
## 7. Gates
|
||||
|
||||
- [x] 7.1 `bunx turbo lint typecheck test --filter=...` green.
|
||||
- [x] 7.2 `bunx turbo test:coverage --filter=...` at 100/100/100/100.
|
||||
- [x] 7.3 `bun run docs:check` reports 5/5 files.
|
||||
Reference in New Issue
Block a user