streamkit-binary-stream 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +212 -0
- package/dist/binaryStream.d.ts +35 -0
- package/dist/binaryStream.d.ts.map +1 -0
- package/dist/binaryStream.js +180 -0
- package/dist/binaryStream.js.map +1 -0
- package/dist/bufferQueue.d.ts +59 -0
- package/dist/bufferQueue.d.ts.map +1 -0
- package/dist/bufferQueue.js +143 -0
- package/dist/bufferQueue.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +59 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 StreamKit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# @streamkit/binary-stream
|
|
2
|
+
|
|
3
|
+
Transport-agnostic binary frame streaming layer for real-time applications.
|
|
4
|
+
|
|
5
|
+
## What This Package Solves
|
|
6
|
+
|
|
7
|
+
Real-time binary data (point clouds, video metadata, telemetry) arrives in arbitrary chunks from various sources:
|
|
8
|
+
|
|
9
|
+
- WebRTC DataChannels
|
|
10
|
+
- WebSockets
|
|
11
|
+
- File readers
|
|
12
|
+
- Serial ports
|
|
13
|
+
- Sensor APIs
|
|
14
|
+
|
|
15
|
+
This package reassembles those chunks into complete, framed messages with:
|
|
16
|
+
|
|
17
|
+
- **Deterministic frame boundaries** via a simple header protocol
|
|
18
|
+
- **Backpressure handling** to prevent unbounded memory growth
|
|
19
|
+
- **Drop strategies** for real-time scenarios where latency matters more than completeness
|
|
20
|
+
|
|
21
|
+
## Why It Exists
|
|
22
|
+
|
|
23
|
+
Separating frame reassembly from transport and decoding enables:
|
|
24
|
+
|
|
25
|
+
- **Transport flexibility**: Same framing logic for WebRTC, WebSocket, or any other source
|
|
26
|
+
- **Decoder independence**: Frames can carry point clouds, video metadata, or any binary format
|
|
27
|
+
- **Testability**: Pure synchronous logic, no mocking of network APIs
|
|
28
|
+
|
|
29
|
+
## How It Fits with @streamkit/pointcloud
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
┌─────────────────────┐
|
|
33
|
+
│ Transport Layer │ ← WebRTC, WebSocket, File, etc.
|
|
34
|
+
│ (your code) │
|
|
35
|
+
└──────────┬──────────┘
|
|
36
|
+
│ ArrayBuffer chunks
|
|
37
|
+
▼
|
|
38
|
+
┌─────────────────────┐
|
|
39
|
+
│ @streamkit/ │ ← This package
|
|
40
|
+
│ binary-stream │
|
|
41
|
+
└──────────┬──────────┘
|
|
42
|
+
│ BinaryFrame { id, timestamp, payload }
|
|
43
|
+
▼
|
|
44
|
+
┌─────────────────────┐
|
|
45
|
+
│ @streamkit/ │ ← Point cloud decoding
|
|
46
|
+
│ pointcloud │
|
|
47
|
+
└──────────┬──────────┘
|
|
48
|
+
│ PointCloudData
|
|
49
|
+
▼
|
|
50
|
+
┌─────────────────────┐
|
|
51
|
+
│ Three.js Layer │ ← Rendering
|
|
52
|
+
└─────────────────────┘
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm install @streamkit/binary-stream
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { createBinaryStream } from "@streamkit/binary-stream";
|
|
65
|
+
|
|
66
|
+
const stream = createBinaryStream({
|
|
67
|
+
maxFrameSize: 2_000_000, // 2MB max per frame
|
|
68
|
+
maxBufferSize: 10_000_000, // 10MB buffer limit
|
|
69
|
+
dropStrategy: "oldest",
|
|
70
|
+
onFrame(frame) {
|
|
71
|
+
console.log("Frame received:", frame.id, frame.payload.byteLength);
|
|
72
|
+
// Pass to decoder, e.g., @streamkit/pointcloud
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Feed from ANY source
|
|
77
|
+
dataChannel.onmessage = (e) => stream.push(e.data);
|
|
78
|
+
// or
|
|
79
|
+
socket.onmessage = (e) => stream.push(e.data);
|
|
80
|
+
// or
|
|
81
|
+
fileReader.onload = (e) => stream.push(e.target.result);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Frame Protocol
|
|
85
|
+
|
|
86
|
+
Frames use a simple binary header (little-endian):
|
|
87
|
+
|
|
88
|
+
| Offset | Size | Type | Description |
|
|
89
|
+
| ------ | ---- | ------ | -------------- |
|
|
90
|
+
| 0 | 4 | uint32 | Payload length |
|
|
91
|
+
| 4 | 4 | uint32 | Frame ID |
|
|
92
|
+
| 8 | N | bytes | Payload |
|
|
93
|
+
|
|
94
|
+
Total header size: **8 bytes**
|
|
95
|
+
|
|
96
|
+
### Encoding Example (sender side)
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
function encodeFrame(id: number, payload: ArrayBuffer): ArrayBuffer {
|
|
100
|
+
const header = new ArrayBuffer(8);
|
|
101
|
+
const view = new DataView(header);
|
|
102
|
+
view.setUint32(0, payload.byteLength, true); // little-endian
|
|
103
|
+
view.setUint32(4, id, true);
|
|
104
|
+
|
|
105
|
+
const frame = new Uint8Array(8 + payload.byteLength);
|
|
106
|
+
frame.set(new Uint8Array(header), 0);
|
|
107
|
+
frame.set(new Uint8Array(payload), 8);
|
|
108
|
+
return frame.buffer;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## API Reference
|
|
113
|
+
|
|
114
|
+
### `createBinaryStream(options)`
|
|
115
|
+
|
|
116
|
+
Creates a binary frame stream processor.
|
|
117
|
+
|
|
118
|
+
**Options:**
|
|
119
|
+
|
|
120
|
+
| Option | Type | Default | Description |
|
|
121
|
+
| --------------- | ------------------------------ | ---------- | -------------------------------------- |
|
|
122
|
+
| `maxFrameSize` | `number` | 10MB | Maximum allowed frame payload size |
|
|
123
|
+
| `maxBufferSize` | `number` | 50MB | Maximum buffered bytes before dropping |
|
|
124
|
+
| `dropStrategy` | `'oldest' \| 'newest'` | `'oldest'` | Which data to drop on overflow |
|
|
125
|
+
| `onFrame` | `(frame: BinaryFrame) => void` | required | Frame completion callback |
|
|
126
|
+
|
|
127
|
+
**Returns:** `BinaryStream`
|
|
128
|
+
|
|
129
|
+
### `BinaryStream`
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
interface BinaryStream {
|
|
133
|
+
push(chunk: ArrayBuffer): void; // Feed data
|
|
134
|
+
reset(): void; // Clear buffers, reset state
|
|
135
|
+
dispose(): void; // Release resources
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### `BinaryFrame`
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
interface BinaryFrame {
|
|
143
|
+
id: number; // Frame ID from header
|
|
144
|
+
timestamp: number; // Completion time (performance.now())
|
|
145
|
+
payload: ArrayBuffer; // Raw payload data
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Drop Strategies
|
|
150
|
+
|
|
151
|
+
### `'oldest'` (default)
|
|
152
|
+
|
|
153
|
+
When buffer exceeds `maxBufferSize`:
|
|
154
|
+
|
|
155
|
+
- Drops oldest buffered data
|
|
156
|
+
- Resets parse state (may lose partial frame)
|
|
157
|
+
- Best for: real-time streaming where latest data matters
|
|
158
|
+
|
|
159
|
+
### `'newest'`
|
|
160
|
+
|
|
161
|
+
When buffer exceeds `maxBufferSize`:
|
|
162
|
+
|
|
163
|
+
- Rejects new incoming chunks
|
|
164
|
+
- Preserves existing buffered data
|
|
165
|
+
- Best for: scenarios where completing current frame is priority
|
|
166
|
+
|
|
167
|
+
## Memory Management
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// Reset on connection loss
|
|
171
|
+
stream.reset();
|
|
172
|
+
|
|
173
|
+
// Full cleanup
|
|
174
|
+
stream.dispose();
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
After `dispose()`, the stream cannot be reused.
|
|
178
|
+
|
|
179
|
+
## Error Handling
|
|
180
|
+
|
|
181
|
+
- **Oversized frames**: Logged and skipped (header consumed, payload discarded)
|
|
182
|
+
- **Callback errors**: Logged but don't break the stream
|
|
183
|
+
- **Use after dispose**: Throws error
|
|
184
|
+
|
|
185
|
+
## TypeScript
|
|
186
|
+
|
|
187
|
+
Full type exports:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import type {
|
|
191
|
+
BinaryFrame,
|
|
192
|
+
BinaryStream,
|
|
193
|
+
BinaryStreamOptions,
|
|
194
|
+
DropStrategy,
|
|
195
|
+
} from "@streamkit/binary-stream";
|
|
196
|
+
|
|
197
|
+
import { FRAME_HEADER_SIZE } from "@streamkit/binary-stream";
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Design Constraints
|
|
201
|
+
|
|
202
|
+
This package intentionally does NOT:
|
|
203
|
+
|
|
204
|
+
- ❌ Perform any networking
|
|
205
|
+
- ❌ Decode payload contents
|
|
206
|
+
- ❌ Use async/await or Promises
|
|
207
|
+
- ❌ Emit events or use Observables
|
|
208
|
+
- ❌ Depend on Node.js or browser-specific APIs (except `performance.now()`)
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streamkit/binary-stream - Binary Stream Implementation
|
|
3
|
+
*
|
|
4
|
+
* Transport-agnostic binary frame reassembly with backpressure support.
|
|
5
|
+
*/
|
|
6
|
+
import type { BinaryStream, BinaryStreamOptions } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Creates a transport-agnostic binary stream for frame reassembly.
|
|
9
|
+
*
|
|
10
|
+
* The stream accepts arbitrary binary chunks and reassembles them into
|
|
11
|
+
* complete frames based on a simple header protocol:
|
|
12
|
+
* - 4 bytes: payload length (uint32 LE)
|
|
13
|
+
* - 4 bytes: frame ID (uint32 LE)
|
|
14
|
+
* - N bytes: payload
|
|
15
|
+
*
|
|
16
|
+
* @param options - Stream configuration
|
|
17
|
+
* @returns BinaryStream controller
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const stream = createBinaryStream({
|
|
22
|
+
* maxFrameSize: 2_000_000,
|
|
23
|
+
* dropStrategy: 'oldest',
|
|
24
|
+
* onFrame(frame) {
|
|
25
|
+
* console.log('Frame:', frame.id, frame.payload.byteLength);
|
|
26
|
+
* }
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Feed from any source
|
|
30
|
+
* stream.push(chunk1);
|
|
31
|
+
* stream.push(chunk2);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function createBinaryStream(options: BinaryStreamOptions): BinaryStream;
|
|
35
|
+
//# sourceMappingURL=binaryStream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binaryStream.d.ts","sourceRoot":"","sources":["../src/binaryStream.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAEV,YAAY,EACZ,mBAAmB,EAEpB,MAAM,YAAY,CAAC;AAmCpB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CA2J7E"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streamkit/binary-stream - Binary Stream Implementation
|
|
3
|
+
*
|
|
4
|
+
* Transport-agnostic binary frame reassembly with backpressure support.
|
|
5
|
+
*/
|
|
6
|
+
import { BufferQueue } from "./bufferQueue.js";
|
|
7
|
+
import { FRAME_HEADER_SIZE } from "./types.js";
|
|
8
|
+
/** Default maximum frame size: 10MB */
|
|
9
|
+
const DEFAULT_MAX_FRAME_SIZE = 10 * 1024 * 1024;
|
|
10
|
+
/** Default maximum buffer size: 50MB */
|
|
11
|
+
const DEFAULT_MAX_BUFFER_SIZE = 50 * 1024 * 1024;
|
|
12
|
+
/** Default drop strategy */
|
|
13
|
+
const DEFAULT_DROP_STRATEGY = "oldest";
|
|
14
|
+
/**
|
|
15
|
+
* Reads a uint32 from a Uint8Array at offset (little-endian).
|
|
16
|
+
*/
|
|
17
|
+
function readUint32LE(data, offset) {
|
|
18
|
+
return ((data[offset] |
|
|
19
|
+
(data[offset + 1] << 8) |
|
|
20
|
+
(data[offset + 2] << 16) |
|
|
21
|
+
(data[offset + 3] << 24)) >>>
|
|
22
|
+
0); // >>> 0 ensures unsigned
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a transport-agnostic binary stream for frame reassembly.
|
|
26
|
+
*
|
|
27
|
+
* The stream accepts arbitrary binary chunks and reassembles them into
|
|
28
|
+
* complete frames based on a simple header protocol:
|
|
29
|
+
* - 4 bytes: payload length (uint32 LE)
|
|
30
|
+
* - 4 bytes: frame ID (uint32 LE)
|
|
31
|
+
* - N bytes: payload
|
|
32
|
+
*
|
|
33
|
+
* @param options - Stream configuration
|
|
34
|
+
* @returns BinaryStream controller
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const stream = createBinaryStream({
|
|
39
|
+
* maxFrameSize: 2_000_000,
|
|
40
|
+
* dropStrategy: 'oldest',
|
|
41
|
+
* onFrame(frame) {
|
|
42
|
+
* console.log('Frame:', frame.id, frame.payload.byteLength);
|
|
43
|
+
* }
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* // Feed from any source
|
|
47
|
+
* stream.push(chunk1);
|
|
48
|
+
* stream.push(chunk2);
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function createBinaryStream(options) {
|
|
52
|
+
const { maxFrameSize = DEFAULT_MAX_FRAME_SIZE, maxBufferSize = DEFAULT_MAX_BUFFER_SIZE, dropStrategy = DEFAULT_DROP_STRATEGY, onFrame, } = options;
|
|
53
|
+
// Validate options
|
|
54
|
+
if (typeof onFrame !== "function") {
|
|
55
|
+
throw new Error("BinaryStream: onFrame callback is required");
|
|
56
|
+
}
|
|
57
|
+
if (maxFrameSize <= 0) {
|
|
58
|
+
throw new Error("BinaryStream: maxFrameSize must be positive");
|
|
59
|
+
}
|
|
60
|
+
if (maxBufferSize < maxFrameSize) {
|
|
61
|
+
throw new Error("BinaryStream: maxBufferSize must be >= maxFrameSize");
|
|
62
|
+
}
|
|
63
|
+
// Internal state
|
|
64
|
+
const queue = new BufferQueue();
|
|
65
|
+
let parseState = { expectedLength: -1, frameId: 0 };
|
|
66
|
+
let disposed = false;
|
|
67
|
+
let droppedFrames = 0;
|
|
68
|
+
/**
|
|
69
|
+
* Validates stream is not disposed.
|
|
70
|
+
*/
|
|
71
|
+
function assertNotDisposed() {
|
|
72
|
+
if (disposed) {
|
|
73
|
+
throw new Error("BinaryStream has been disposed");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Applies backpressure by dropping frames according to strategy.
|
|
78
|
+
*/
|
|
79
|
+
function applyBackpressure() {
|
|
80
|
+
if (queue.byteLength <= maxBufferSize)
|
|
81
|
+
return;
|
|
82
|
+
const overflow = queue.byteLength - maxBufferSize;
|
|
83
|
+
if (dropStrategy === "oldest") {
|
|
84
|
+
// Drop oldest buffered data
|
|
85
|
+
// Reset parse state since we're corrupting the stream
|
|
86
|
+
const dropped = queue.dropOldest(overflow);
|
|
87
|
+
if (dropped > 0) {
|
|
88
|
+
parseState = { expectedLength: -1, frameId: 0 };
|
|
89
|
+
droppedFrames++;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// 'newest' strategy: handled in push() by rejecting new chunks
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Attempts to parse complete frames from the buffer.
|
|
96
|
+
*/
|
|
97
|
+
function processFrames() {
|
|
98
|
+
while (!disposed) {
|
|
99
|
+
// State: waiting for header
|
|
100
|
+
if (parseState.expectedLength === -1) {
|
|
101
|
+
if (!queue.hasBytes(FRAME_HEADER_SIZE)) {
|
|
102
|
+
break; // Need more data
|
|
103
|
+
}
|
|
104
|
+
const header = queue.peek(FRAME_HEADER_SIZE);
|
|
105
|
+
if (!header)
|
|
106
|
+
break;
|
|
107
|
+
const payloadLength = readUint32LE(header, 0);
|
|
108
|
+
const frameId = readUint32LE(header, 4);
|
|
109
|
+
// Validate frame size
|
|
110
|
+
if (payloadLength > maxFrameSize) {
|
|
111
|
+
// Frame too large - skip header and try to resync
|
|
112
|
+
queue.skip(FRAME_HEADER_SIZE);
|
|
113
|
+
console.warn(`BinaryStream: Frame ${frameId} exceeds maxFrameSize (${payloadLength} > ${maxFrameSize}), skipping`);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
// Consume header and update state
|
|
117
|
+
queue.skip(FRAME_HEADER_SIZE);
|
|
118
|
+
parseState = { expectedLength: payloadLength, frameId };
|
|
119
|
+
}
|
|
120
|
+
// State: waiting for payload
|
|
121
|
+
if (parseState.expectedLength >= 0) {
|
|
122
|
+
if (!queue.hasBytes(parseState.expectedLength)) {
|
|
123
|
+
break; // Need more data
|
|
124
|
+
}
|
|
125
|
+
// Read complete payload
|
|
126
|
+
const payload = queue.read(parseState.expectedLength);
|
|
127
|
+
if (!payload)
|
|
128
|
+
break; // Should not happen
|
|
129
|
+
// Emit frame
|
|
130
|
+
const frame = {
|
|
131
|
+
id: parseState.frameId,
|
|
132
|
+
timestamp: performance.now(),
|
|
133
|
+
payload,
|
|
134
|
+
};
|
|
135
|
+
// Reset parse state for next frame
|
|
136
|
+
parseState = { expectedLength: -1, frameId: 0 };
|
|
137
|
+
// Invoke callback (synchronously)
|
|
138
|
+
try {
|
|
139
|
+
onFrame(frame);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
console.error("BinaryStream: onFrame callback threw:", err);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
push(chunk) {
|
|
149
|
+
assertNotDisposed();
|
|
150
|
+
if (chunk.byteLength === 0)
|
|
151
|
+
return;
|
|
152
|
+
// 'newest' drop strategy: reject if adding would exceed buffer
|
|
153
|
+
if (dropStrategy === "newest") {
|
|
154
|
+
if (queue.byteLength + chunk.byteLength > maxBufferSize) {
|
|
155
|
+
droppedFrames++;
|
|
156
|
+
return; // Drop this chunk
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Add chunk to queue
|
|
160
|
+
queue.push(chunk);
|
|
161
|
+
// Apply backpressure for 'oldest' strategy
|
|
162
|
+
applyBackpressure();
|
|
163
|
+
// Process any complete frames
|
|
164
|
+
processFrames();
|
|
165
|
+
},
|
|
166
|
+
reset() {
|
|
167
|
+
assertNotDisposed();
|
|
168
|
+
queue.clear();
|
|
169
|
+
parseState = { expectedLength: -1, frameId: 0 };
|
|
170
|
+
},
|
|
171
|
+
dispose() {
|
|
172
|
+
if (disposed)
|
|
173
|
+
return;
|
|
174
|
+
disposed = true;
|
|
175
|
+
queue.clear();
|
|
176
|
+
parseState = { expectedLength: -1, frameId: 0 };
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=binaryStream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binaryStream.js","sourceRoot":"","sources":["../src/binaryStream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streamkit/binary-stream - Buffer Queue
|
|
3
|
+
*
|
|
4
|
+
* Efficient buffer management for chunk accumulation.
|
|
5
|
+
* Supports partial reads and memory-bounded operation.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* A queue that accumulates ArrayBuffer chunks and supports
|
|
9
|
+
* sequential reading of arbitrary byte lengths.
|
|
10
|
+
*/
|
|
11
|
+
export declare class BufferQueue {
|
|
12
|
+
private chunks;
|
|
13
|
+
private totalBytes;
|
|
14
|
+
private readOffset;
|
|
15
|
+
/**
|
|
16
|
+
* Current number of bytes available for reading.
|
|
17
|
+
*/
|
|
18
|
+
get byteLength(): number;
|
|
19
|
+
/**
|
|
20
|
+
* Append a chunk to the queue.
|
|
21
|
+
* @param buffer - Data to append
|
|
22
|
+
*/
|
|
23
|
+
push(buffer: ArrayBuffer): void;
|
|
24
|
+
/**
|
|
25
|
+
* Check if at least `length` bytes are available.
|
|
26
|
+
* @param length - Required byte count
|
|
27
|
+
*/
|
|
28
|
+
hasBytes(length: number): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Peek at bytes without consuming them.
|
|
31
|
+
* @param length - Number of bytes to peek
|
|
32
|
+
* @returns Uint8Array view or null if insufficient data
|
|
33
|
+
*/
|
|
34
|
+
peek(length: number): Uint8Array | null;
|
|
35
|
+
/**
|
|
36
|
+
* Read and consume bytes from the queue.
|
|
37
|
+
* @param length - Number of bytes to read
|
|
38
|
+
* @returns ArrayBuffer or null if insufficient data
|
|
39
|
+
*/
|
|
40
|
+
read(length: number): ArrayBuffer | null;
|
|
41
|
+
/**
|
|
42
|
+
* Skip bytes without copying them.
|
|
43
|
+
* @param length - Number of bytes to skip
|
|
44
|
+
* @returns True if bytes were skipped, false if insufficient data
|
|
45
|
+
*/
|
|
46
|
+
skip(length: number): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Drop all data from the oldest incomplete frame.
|
|
49
|
+
* Removes bytes until the queue is empty or below threshold.
|
|
50
|
+
* @param bytesToDrop - Minimum bytes to remove
|
|
51
|
+
* @returns Number of bytes actually dropped
|
|
52
|
+
*/
|
|
53
|
+
dropOldest(bytesToDrop: number): number;
|
|
54
|
+
/**
|
|
55
|
+
* Clear all buffered data.
|
|
56
|
+
*/
|
|
57
|
+
clear(): void;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=bufferQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bufferQueue.d.ts","sourceRoot":"","sources":["../src/bufferQueue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,UAAU,CAAK;IAEvB;;OAEG;IACH,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED;;;OAGG;IACH,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAM/B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIjC;;;;OAIG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IA2BvC;;;;OAIG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IA8BxC;;;;OAIG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAwB7B;;;;;OAKG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAQvC;;OAEG;IACH,KAAK,IAAI,IAAI;CAKd"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streamkit/binary-stream - Buffer Queue
|
|
3
|
+
*
|
|
4
|
+
* Efficient buffer management for chunk accumulation.
|
|
5
|
+
* Supports partial reads and memory-bounded operation.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* A queue that accumulates ArrayBuffer chunks and supports
|
|
9
|
+
* sequential reading of arbitrary byte lengths.
|
|
10
|
+
*/
|
|
11
|
+
export class BufferQueue {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.chunks = [];
|
|
14
|
+
this.totalBytes = 0;
|
|
15
|
+
this.readOffset = 0; // Offset within first chunk
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Current number of bytes available for reading.
|
|
19
|
+
*/
|
|
20
|
+
get byteLength() {
|
|
21
|
+
return this.totalBytes;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Append a chunk to the queue.
|
|
25
|
+
* @param buffer - Data to append
|
|
26
|
+
*/
|
|
27
|
+
push(buffer) {
|
|
28
|
+
if (buffer.byteLength === 0)
|
|
29
|
+
return;
|
|
30
|
+
this.chunks.push(new Uint8Array(buffer));
|
|
31
|
+
this.totalBytes += buffer.byteLength;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if at least `length` bytes are available.
|
|
35
|
+
* @param length - Required byte count
|
|
36
|
+
*/
|
|
37
|
+
hasBytes(length) {
|
|
38
|
+
return this.totalBytes >= length;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Peek at bytes without consuming them.
|
|
42
|
+
* @param length - Number of bytes to peek
|
|
43
|
+
* @returns Uint8Array view or null if insufficient data
|
|
44
|
+
*/
|
|
45
|
+
peek(length) {
|
|
46
|
+
if (!this.hasBytes(length))
|
|
47
|
+
return null;
|
|
48
|
+
const result = new Uint8Array(length);
|
|
49
|
+
let written = 0;
|
|
50
|
+
let chunkIndex = 0;
|
|
51
|
+
let offset = this.readOffset;
|
|
52
|
+
while (written < length && chunkIndex < this.chunks.length) {
|
|
53
|
+
const chunk = this.chunks[chunkIndex];
|
|
54
|
+
const available = chunk.length - offset;
|
|
55
|
+
const toCopy = Math.min(available, length - written);
|
|
56
|
+
result.set(chunk.subarray(offset, offset + toCopy), written);
|
|
57
|
+
written += toCopy;
|
|
58
|
+
if (offset + toCopy >= chunk.length) {
|
|
59
|
+
chunkIndex++;
|
|
60
|
+
offset = 0;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
offset += toCopy;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Read and consume bytes from the queue.
|
|
70
|
+
* @param length - Number of bytes to read
|
|
71
|
+
* @returns ArrayBuffer or null if insufficient data
|
|
72
|
+
*/
|
|
73
|
+
read(length) {
|
|
74
|
+
if (!this.hasBytes(length))
|
|
75
|
+
return null;
|
|
76
|
+
const result = new Uint8Array(length);
|
|
77
|
+
let written = 0;
|
|
78
|
+
while (written < length) {
|
|
79
|
+
const chunk = this.chunks[0];
|
|
80
|
+
const available = chunk.length - this.readOffset;
|
|
81
|
+
const toCopy = Math.min(available, length - written);
|
|
82
|
+
result.set(chunk.subarray(this.readOffset, this.readOffset + toCopy), written);
|
|
83
|
+
written += toCopy;
|
|
84
|
+
this.totalBytes -= toCopy;
|
|
85
|
+
if (this.readOffset + toCopy >= chunk.length) {
|
|
86
|
+
// Consumed entire chunk, remove it
|
|
87
|
+
this.chunks.shift();
|
|
88
|
+
this.readOffset = 0;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
this.readOffset += toCopy;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result.buffer;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Skip bytes without copying them.
|
|
98
|
+
* @param length - Number of bytes to skip
|
|
99
|
+
* @returns True if bytes were skipped, false if insufficient data
|
|
100
|
+
*/
|
|
101
|
+
skip(length) {
|
|
102
|
+
if (!this.hasBytes(length))
|
|
103
|
+
return false;
|
|
104
|
+
let remaining = length;
|
|
105
|
+
while (remaining > 0) {
|
|
106
|
+
const chunk = this.chunks[0];
|
|
107
|
+
const available = chunk.length - this.readOffset;
|
|
108
|
+
const toSkip = Math.min(available, remaining);
|
|
109
|
+
remaining -= toSkip;
|
|
110
|
+
this.totalBytes -= toSkip;
|
|
111
|
+
if (this.readOffset + toSkip >= chunk.length) {
|
|
112
|
+
this.chunks.shift();
|
|
113
|
+
this.readOffset = 0;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
this.readOffset += toSkip;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Drop all data from the oldest incomplete frame.
|
|
123
|
+
* Removes bytes until the queue is empty or below threshold.
|
|
124
|
+
* @param bytesToDrop - Minimum bytes to remove
|
|
125
|
+
* @returns Number of bytes actually dropped
|
|
126
|
+
*/
|
|
127
|
+
dropOldest(bytesToDrop) {
|
|
128
|
+
const toDrop = Math.min(bytesToDrop, this.totalBytes);
|
|
129
|
+
if (toDrop > 0) {
|
|
130
|
+
this.skip(toDrop);
|
|
131
|
+
}
|
|
132
|
+
return toDrop;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Clear all buffered data.
|
|
136
|
+
*/
|
|
137
|
+
clear() {
|
|
138
|
+
this.chunks = [];
|
|
139
|
+
this.totalBytes = 0;
|
|
140
|
+
this.readOffset = 0;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=bufferQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bufferQueue.js","sourceRoot":"","sources":["../src/bufferQueue.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streamkit/binary-stream
|
|
3
|
+
*
|
|
4
|
+
* Transport-agnostic binary frame streaming layer.
|
|
5
|
+
*
|
|
6
|
+
* This package provides:
|
|
7
|
+
* - Binary frame reassembly from arbitrary chunks
|
|
8
|
+
* - Backpressure with configurable drop strategies
|
|
9
|
+
* - Memory-bounded operation for real-time safety
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
export type { BinaryFrame, BinaryStream, BinaryStreamOptions, DropStrategy, } from "./types.js";
|
|
14
|
+
export { FRAME_HEADER_SIZE } from "./types.js";
|
|
15
|
+
export { createBinaryStream } from "./binaryStream.js";
|
|
16
|
+
export { BufferQueue } from "./bufferQueue.js";
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,YAAY,EACV,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAM/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAMvD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streamkit/binary-stream
|
|
3
|
+
*
|
|
4
|
+
* Transport-agnostic binary frame streaming layer.
|
|
5
|
+
*
|
|
6
|
+
* This package provides:
|
|
7
|
+
* - Binary frame reassembly from arbitrary chunks
|
|
8
|
+
* - Backpressure with configurable drop strategies
|
|
9
|
+
* - Memory-bounded operation for real-time safety
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
export { FRAME_HEADER_SIZE } from "./types.js";
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Stream Factory
|
|
16
|
+
// =============================================================================
|
|
17
|
+
export { createBinaryStream } from "./binaryStream.js";
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Utilities (internal, but exported for advanced use)
|
|
20
|
+
// =============================================================================
|
|
21
|
+
export { BufferQueue } from "./bufferQueue.js";
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAaH,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAEhF,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,gFAAgF;AAChF,sDAAsD;AACtD,gFAAgF;AAEhF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streamkit/binary-stream - Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Core types for the binary frame streaming layer.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* A complete binary frame extracted from the stream.
|
|
8
|
+
*/
|
|
9
|
+
export interface BinaryFrame {
|
|
10
|
+
/** Unique frame identifier from the stream */
|
|
11
|
+
id: number;
|
|
12
|
+
/** Timestamp when frame was completed (performance.now()) */
|
|
13
|
+
timestamp: number;
|
|
14
|
+
/** Raw payload data */
|
|
15
|
+
payload: ArrayBuffer;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Drop strategy when buffer limits are exceeded.
|
|
19
|
+
*/
|
|
20
|
+
export type DropStrategy = "oldest" | "newest";
|
|
21
|
+
/**
|
|
22
|
+
* Configuration options for creating a binary stream.
|
|
23
|
+
*/
|
|
24
|
+
export interface BinaryStreamOptions {
|
|
25
|
+
/** Maximum allowed frame size in bytes (default: 10MB) */
|
|
26
|
+
maxFrameSize?: number;
|
|
27
|
+
/** Maximum buffer size in bytes before applying drop strategy (default: 50MB) */
|
|
28
|
+
maxBufferSize?: number;
|
|
29
|
+
/** Strategy for dropping frames when buffer overflows (default: 'oldest') */
|
|
30
|
+
dropStrategy?: DropStrategy;
|
|
31
|
+
/** Callback invoked when a complete frame is ready */
|
|
32
|
+
onFrame: (frame: BinaryFrame) => void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Binary stream controller interface.
|
|
36
|
+
*/
|
|
37
|
+
export interface BinaryStream {
|
|
38
|
+
/**
|
|
39
|
+
* Push a chunk of binary data into the stream.
|
|
40
|
+
* Chunks are accumulated and reassembled into complete frames.
|
|
41
|
+
* @param chunk - Raw binary data from any source
|
|
42
|
+
*/
|
|
43
|
+
push(chunk: ArrayBuffer): void;
|
|
44
|
+
/**
|
|
45
|
+
* Reset the stream state, discarding all buffered data.
|
|
46
|
+
* Use after connection reset or stream discontinuity.
|
|
47
|
+
*/
|
|
48
|
+
reset(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Dispose the stream and release all resources.
|
|
51
|
+
* Stream cannot be used after disposal.
|
|
52
|
+
*/
|
|
53
|
+
dispose(): void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Frame header constants.
|
|
57
|
+
*/
|
|
58
|
+
export declare const FRAME_HEADER_SIZE = 8;
|
|
59
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,sDAAsD;IACtD,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IAE/B;;;OAGG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAyDH;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,CAAC,8BAA8B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "streamkit-binary-stream",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Transport-agnostic binary frame streaming layer for real-time applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"clean": "rimraf dist",
|
|
22
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"binary",
|
|
26
|
+
"stream",
|
|
27
|
+
"frame",
|
|
28
|
+
"buffer",
|
|
29
|
+
"real-time",
|
|
30
|
+
"streaming",
|
|
31
|
+
"arraybuffer",
|
|
32
|
+
"backpressure"
|
|
33
|
+
],
|
|
34
|
+
"author": "",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"rimraf": "^5.0.5",
|
|
38
|
+
"typescript": "^5.3.0"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0"
|
|
42
|
+
}
|
|
43
|
+
}
|