insite-ws-transfers
Version:
File transfers over WebSockets for inSite
259 lines (190 loc) • 9.18 kB
Markdown
# insite-ws-transfers
File transfers over WebSockets for the inSite stack. Streams data in chunks with progress tracking, abort support, and receiver confirmation.
Part of [inSite](../../README.md) — built on [insite-ws](../ws/README.md). Requires `WSServer` or `WS` from that package.
## Installation
```sh
npm install insite-ws-transfers
```
Or:
```sh
bun add insite-ws-transfers
```
## Overview
**Entry points:**
| Entry | Environment | Description |
|-------|-------------|-------------|
| `insite-ws-transfers` | Node.js | Default; same as `/node` |
| `insite-ws-transfers/node` | Node.js | Incoming/outgoing with streams and file paths |
| `insite-ws-transfers/browser` | Browser | Incoming/outgoing with `File` API |
**Protocol flow:** Sender sends a transfer request → Receiver confirms (or rejects via `begin` returning `false`) → Chunks stream over the wire → Progress updates every 250 ms → Completion, abort, or error.
**Transfer types:** `object` (JSON), `string`, `datauri`, `file`, `stream` (Node only).
## Usage
### Node server (receiving)
```ts
import { WSServer } from "insite-ws/server";
import { IncomingTransport } from "insite-ws-transfers";
const wss = new WSServer({ port: 8080 });
new IncomingTransport(wss, { sizeLimit: 50 * 1024 * 1024 });
wss.onTransfer("file", {
begin(wssc, transfer) {
// Return false to reject
},
end(wssc, transfer) {
// transfer.data is ready (Buffer, string, or parsed object)
}
});
```
### Node server (sending)
```ts
import { OutgoingTransport } from "insite-ws-transfers";
new OutgoingTransport(wss);
const transfer = wss.transfer(wssc, "file", { data: "/path/to/file.pdf" });
```
### Browser client
```ts
import { WS } from "insite-ws/client";
import { IncomingTransport, OutgoingTransport } from "insite-ws-transfers/browser";
const ws = new WS({ url: "wss://example.com" });
new IncomingTransport(ws);
new OutgoingTransport(ws);
ws.onTransfer("file", { end(transfer) { console.log(transfer.data); } });
ws.transfer("file", { data: fileInput.files[0] });
```
## Transfer types by environment
| Type | Node (out) | Node (in) | Browser (out) | Browser (in) |
|------|------------|-----------|---------------|---------------|
| object | ✓ | ✓ | ✓ | ✓ |
| string | ✓ | ✓ | ✓ | ✓ |
| datauri | ✓ | ✓ | ✓ | ✓ |
| file | File path | Stream / Buffer | `File` | Base64 string |
| stream | `Readable` | Stream / Buffer | — | — |
## API Reference
### IncomingTransport
Handles incoming transfers. Attaches `onTransfer` and `onceTransfer` to the WS instance.
**Constructor:** `new IncomingTransport(ws, options?)`
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `sizeLimit` | `number` | 10 GB | Max transfer size in bytes |
**Methods:**
| Method | Description |
|--------|-------------|
| `addTransferListener(kind, listener, options?)` | Register handler for transfer kind |
| `on(kind, listener, options?)` | Alias for `addTransferListener` |
| `once(kind, listener, options?)` | One-time listener |
| `removeTransferListener(kind, listener?)` | Unregister; omit `listener` to remove all for kind |
| `off(kind, listener?)` | Alias for `removeTransferListener` |
**Properties:** `sizeLimit` — max transfer size.
### IncomingTransfer
Represents an incoming transfer. Instance of `IncomingTransfer` (browser) or `NodeIncomingTransfer` (Node).
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `ws` | `WS \| WSServerClient` | WebSocket connection |
| `id` | `string` | Transfer ID |
| `kind` | `string` | Transfer kind (e.g. `"file"`) |
| `type` | `TransferTypes` | Data type |
| `collect` | `boolean` | Whether data is buffered |
| `encoding` | `"base64" \| "buffer" \| "utf8"` | Chunk encoding |
| `size` | `number` | Total size in bytes (0 if unknown) |
| `metadata` | `Record<string, unknown>` | Sender metadata |
| `confirmResponse` | `string \| undefined` | Set in `begin` to send custom response to sender |
| `data` | `Buffer \| string \| undefined` | Collected result (when `collect` and transfer done) |
| `progress` | `number` | 0–1 receiver progress |
| `transferedSize` | `number` | Bytes received so far |
| `processedSize` | `number` | Bytes processed so far |
| `bytesPerMs` | `number \| null` | Throughput |
| `duration` | `number \| null` | Transfer duration (ms) |
| `isAborted` | `boolean` | Whether aborted |
| `isAbortedBySender` | `boolean` | Aborted by sender |
| `isAbortedByReceiver` | `boolean` | Aborted by receiver |
| `isTransfered` | `boolean` | All chunks received |
| `error` | `Error \| null` | Error if failed |
**Methods:**
| Method | Description |
|--------|-------------|
| `whenSetUp()` | `Promise<void>` — Resolves when setup and confirm sent |
| `abort(bySender?)` | Abort transfer; `bySender: true` when sender aborted |
| `serialize()` | Returns plain object with transfer state |
**Node only:** `pipeTo(writableStream)` — Pipe incoming stream/file data to a `Writable` stream. Available when `type` is `stream` or `file`.
### OutgoingTransport
Handles outgoing transfers. Attaches `transfer` to the WS instance.
**Constructor:** `new OutgoingTransport(ws)`
**Methods:**
| Method | Description |
|--------|-------------|
| `transfer(ws, kind, props)` | Start transfer. On server, `ws` is `WSServerClient`; on client, use `ws.transfer(kind, props)` |
### OutgoingTransfer
Represents an outgoing transfer.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `ws` | `WS \| WSServerClient` | WebSocket connection |
| `id` | `string` | Transfer ID |
| `kind` | `string` | Transfer kind |
| `type` | `string` | Detected data type |
| `collect` | `boolean` | Whether receiver buffers |
| `metadata` | `Record<string, unknown>` | Custom metadata |
| `size` | `number \| null` | Total size (null if unknown) |
| `senderProgress` | `number` | 0–1 sender progress |
| `progress` | `number` | 0–1 receiver progress |
| `transferedSize` | `number` | Bytes sent |
| `bytesPerMs` | `number \| null` | Throughput |
| `duration` | `number \| null` | Transfer duration (ms) |
| `isAborted` | `boolean` | Whether aborted |
| `isTransfered` | `boolean` | Transfer completed |
| `error` | `Error \| null` | Error if failed |
**Methods:**
| Method | Description |
|--------|-------------|
| `whenSetUp()` | `Promise<void>` — Resolves when request sent |
| `abort()` | Abort transfer |
| `serialize()` | Returns plain object with transfer state |
### Types
**IncomingTransportOptions**
| Property | Type | Description |
|----------|------|-------------|
| `sizeLimit?` | `number` | Max transfer size in bytes |
**IncomingTransferListener**
| Property | Signature | Description |
|----------|-----------|-------------|
| `begin?` | `(wssc, transfer) => unknown \| false` | Before confirm; return `false` to reject |
| `chunk?` | `(wssc, transfer, chunk) => unknown` | Each chunk received |
| `progress?` | `(wssc, transfer, chunk) => unknown` | Same as chunk |
| `end?` | `(wssc, transfer) => unknown` | Transfer complete |
| `error?` | `(wssc, transfer, error) => unknown` | On error or abort |
| `once?` | `boolean` | Remove listener after first trigger |
On client (`WS`), `wssc` is omitted; listeners receive `(transfer)` or `(transfer, chunk)`.
**IncomingTransferListenerOptions**
| Property | Type | Description |
|----------|------|-------------|
| `once?` | `boolean` | One-time listener |
**OutgoingTransferProps**
| Property | Type | Description |
|----------|------|-------------|
| `data` | `Buffer \| File \| Readable \| object \| string` | Data to send |
| `type?` | `TransferTypes` | Force type; auto-detected if omitted |
| `incomingType?` | `TransferTypes` | Type for receiver |
| `collect?` | `boolean` | Whether receiver buffers (default: `false`) |
| `metadata?` | `Record<string, unknown>` | Custom metadata |
| `size?` | `number` | Total size (for progress) |
| `chunkSize?` | `number` | Chunk size (default: 256 KB) |
| `encoding?` | `"base64" \| "buffer" \| "utf8"` | Chunk encoding |
| `incomingEncoding?` | `"base64" \| "buffer" \| "utf8"` | Encoding for receiver |
| `onBegin?` | `(transfer) => unknown` | After receiver confirmed |
| `onSenderProgress?` | `(transfer) => unknown` | After each chunk sent |
| `onProgress?` | `(transfer) => unknown` | Receiver progress update |
| `onEnd?` | `(transfer) => unknown` | Transfer complete |
| `onError?` | `(transfer, error) => unknown` | On error or abort |
On server, callbacks receive `(wssc, transfer)` or `(wssc, transfer, error)`.
**Type helpers**
| Type | Description |
|------|-------------|
| `WithTransfer<W, WSSC>` | `W` with `transfer(wssc, kind, props)` |
| `WithOptionalTransfer<W, WSSC>` | Partial `transfer` |
| `WithOnTransfer<W, WSSC>` | `W` with `onTransfer` and `onceTransfer` |
| `WithOptionalOnTransfer<W, WSSC>` | Partial `onTransfer` / `onceTransfer` |
## Related
- [insite-ws](../ws/README.md) — provides WSServer and WS
- [insite-server](../server/README.md), [insite-client](../client/README.md), [insite-users-server](../users-server/README.md) — use transfers
## License
MIT