@floegence/flowersec-core 0.1.1

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.
Files changed (120) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +42 -0
  3. package/YAMUX_ALIGNMENT.md +127 -0
  4. package/dist/_examples/flowersec/demo/v1.facade.gen.d.ts +12 -0
  5. package/dist/_examples/flowersec/demo/v1.facade.gen.js +15 -0
  6. package/dist/_examples/flowersec/demo/v1.gen.d.ts +16 -0
  7. package/dist/_examples/flowersec/demo/v1.gen.js +86 -0
  8. package/dist/_examples/flowersec/demo/v1.rpc.gen.d.ts +11 -0
  9. package/dist/_examples/flowersec/demo/v1.rpc.gen.js +22 -0
  10. package/dist/browser/connect.d.ts +12 -0
  11. package/dist/browser/connect.js +31 -0
  12. package/dist/browser/index.d.ts +2 -0
  13. package/dist/browser/index.js +1 -0
  14. package/dist/client-connect/common.d.ts +26 -0
  15. package/dist/client-connect/common.js +167 -0
  16. package/dist/client-connect/connectCore.d.ts +42 -0
  17. package/dist/client-connect/connectCore.js +302 -0
  18. package/dist/client-connect/tunnelAttachCloseReason.d.ts +3 -0
  19. package/dist/client-connect/tunnelAttachCloseReason.js +16 -0
  20. package/dist/client.d.ts +17 -0
  21. package/dist/client.js +1 -0
  22. package/dist/direct-client/connect.d.ts +4 -0
  23. package/dist/direct-client/connect.js +67 -0
  24. package/dist/direct-client/index.d.ts +1 -0
  25. package/dist/direct-client/index.js +1 -0
  26. package/dist/e2ee/constants.d.ts +9 -0
  27. package/dist/e2ee/constants.js +18 -0
  28. package/dist/e2ee/errors.d.ts +5 -0
  29. package/dist/e2ee/errors.js +8 -0
  30. package/dist/e2ee/framing.d.ts +12 -0
  31. package/dist/e2ee/framing.js +57 -0
  32. package/dist/e2ee/handshake.d.ts +80 -0
  33. package/dist/e2ee/handshake.js +322 -0
  34. package/dist/e2ee/index.d.ts +7 -0
  35. package/dist/e2ee/index.js +7 -0
  36. package/dist/e2ee/kdf.d.ts +15 -0
  37. package/dist/e2ee/kdf.js +39 -0
  38. package/dist/e2ee/record.d.ts +11 -0
  39. package/dist/e2ee/record.js +69 -0
  40. package/dist/e2ee/secureChannel.d.ts +82 -0
  41. package/dist/e2ee/secureChannel.js +265 -0
  42. package/dist/e2ee/transcript.d.ts +23 -0
  43. package/dist/e2ee/transcript.js +31 -0
  44. package/dist/facade.d.ts +21 -0
  45. package/dist/facade.js +61 -0
  46. package/dist/gen/flowersec/controlplane/v1.gen.d.ts +36 -0
  47. package/dist/gen/flowersec/controlplane/v1.gen.js +135 -0
  48. package/dist/gen/flowersec/direct/v1.gen.d.ts +21 -0
  49. package/dist/gen/flowersec/direct/v1.gen.js +101 -0
  50. package/dist/gen/flowersec/e2ee/v1.gen.d.ts +68 -0
  51. package/dist/gen/flowersec/e2ee/v1.gen.js +194 -0
  52. package/dist/gen/flowersec/rpc/v1.gen.d.ts +30 -0
  53. package/dist/gen/flowersec/rpc/v1.gen.js +107 -0
  54. package/dist/gen/flowersec/tunnel/v1.gen.d.ts +23 -0
  55. package/dist/gen/flowersec/tunnel/v1.gen.js +104 -0
  56. package/dist/index.d.ts +19 -0
  57. package/dist/index.js +19 -0
  58. package/dist/node/connect.d.ts +9 -0
  59. package/dist/node/connect.js +13 -0
  60. package/dist/node/index.d.ts +2 -0
  61. package/dist/node/index.js +2 -0
  62. package/dist/node/wsFactory.d.ts +2 -0
  63. package/dist/node/wsFactory.js +69 -0
  64. package/dist/observability/index.d.ts +1 -0
  65. package/dist/observability/index.js +1 -0
  66. package/dist/observability/observer.d.ts +23 -0
  67. package/dist/observability/observer.js +28 -0
  68. package/dist/rpc/callError.d.ts +5 -0
  69. package/dist/rpc/callError.js +11 -0
  70. package/dist/rpc/caller.d.ts +8 -0
  71. package/dist/rpc/caller.js +1 -0
  72. package/dist/rpc/client.d.ts +22 -0
  73. package/dist/rpc/client.js +170 -0
  74. package/dist/rpc/framing.d.ts +4 -0
  75. package/dist/rpc/framing.js +24 -0
  76. package/dist/rpc/index.d.ts +6 -0
  77. package/dist/rpc/index.js +6 -0
  78. package/dist/rpc/server.d.ts +15 -0
  79. package/dist/rpc/server.js +67 -0
  80. package/dist/rpc/typed.d.ts +5 -0
  81. package/dist/rpc/typed.js +9 -0
  82. package/dist/rpc/validate.d.ts +2 -0
  83. package/dist/rpc/validate.js +27 -0
  84. package/dist/rpc-proxy/index.d.ts +1 -0
  85. package/dist/rpc-proxy/index.js +1 -0
  86. package/dist/rpc-proxy/rpcProxy.d.ts +13 -0
  87. package/dist/rpc-proxy/rpcProxy.js +59 -0
  88. package/dist/streamhello/index.d.ts +1 -0
  89. package/dist/streamhello/index.js +1 -0
  90. package/dist/streamhello/streamHello.d.ts +3 -0
  91. package/dist/streamhello/streamHello.js +13 -0
  92. package/dist/tunnel-client/connect.d.ts +7 -0
  93. package/dist/tunnel-client/connect.js +125 -0
  94. package/dist/tunnel-client/index.d.ts +1 -0
  95. package/dist/tunnel-client/index.js +1 -0
  96. package/dist/utils/base64url.d.ts +2 -0
  97. package/dist/utils/base64url.js +40 -0
  98. package/dist/utils/bin.d.ts +6 -0
  99. package/dist/utils/bin.js +55 -0
  100. package/dist/utils/errors.d.ts +26 -0
  101. package/dist/utils/errors.js +42 -0
  102. package/dist/utils/number.d.ts +2 -0
  103. package/dist/utils/number.js +9 -0
  104. package/dist/ws/index.d.ts +1 -0
  105. package/dist/ws/index.js +1 -0
  106. package/dist/ws-client/binaryTransport.d.ts +49 -0
  107. package/dist/ws-client/binaryTransport.js +301 -0
  108. package/dist/yamux/byteReader.d.ts +10 -0
  109. package/dist/yamux/byteReader.js +50 -0
  110. package/dist/yamux/constants.d.ts +10 -0
  111. package/dist/yamux/constants.js +14 -0
  112. package/dist/yamux/header.d.ts +17 -0
  113. package/dist/yamux/header.js +26 -0
  114. package/dist/yamux/index.d.ts +5 -0
  115. package/dist/yamux/index.js +5 -0
  116. package/dist/yamux/session.d.ts +44 -0
  117. package/dist/yamux/session.js +228 -0
  118. package/dist/yamux/stream.d.ts +30 -0
  119. package/dist/yamux/stream.js +222 -0
  120. package/package.json +112 -0
@@ -0,0 +1,301 @@
1
+ import { normalizeObserver } from "../observability/observer.js";
2
+ import { AbortError, TimeoutError, throwIfAborted } from "../utils/errors.js";
3
+ export class WsCloseError extends Error {
4
+ code;
5
+ reason;
6
+ constructor(code, reason) {
7
+ const extra = code !== undefined && reason !== undefined && reason !== ""
8
+ ? ` (code=${code} reason=${reason})`
9
+ : code !== undefined
10
+ ? ` (code=${code})`
11
+ : reason !== undefined && reason !== ""
12
+ ? ` (reason=${reason})`
13
+ : "";
14
+ super(`websocket closed${extra}`);
15
+ this.name = "WsCloseError";
16
+ this.code = code;
17
+ this.reason = reason;
18
+ }
19
+ }
20
+ // WebSocketBinaryTransport adapts WebSocket messages to binary reads/writes.
21
+ export class WebSocketBinaryTransport {
22
+ // Underlying WebSocket instance (browser or polyfill).
23
+ ws;
24
+ // Observer for websocket-level errors.
25
+ observer;
26
+ // Buffered inbound frames when no reader is waiting.
27
+ queue = [];
28
+ // Read cursor for queue to avoid Array.shift() O(n).
29
+ queueHead = 0;
30
+ // Current buffered byte count for backpressure.
31
+ queueBytes = 0;
32
+ // Maximum buffered bytes before closing the socket.
33
+ maxQueuedBytes;
34
+ // Pending readers waiting for the next frame.
35
+ waiters = [];
36
+ // Read cursor for waiters to avoid Array.shift() O(n).
37
+ waitersHead = 0;
38
+ // Tracks how many waiters have settled (timeout/abort) without being compacted away yet.
39
+ waitersSettled = 0;
40
+ // Serialize message handling to preserve arrival order across async Blob decoding.
41
+ messageChain = Promise.resolve();
42
+ // Sticky error state to fail all future reads/writes.
43
+ error = null;
44
+ // Tracks whether the close is initiated locally to avoid double-reporting.
45
+ localCloseRequested = false;
46
+ constructor(ws, opts = {}) {
47
+ this.ws = ws;
48
+ this.observer = normalizeObserver(opts.observer);
49
+ this.maxQueuedBytes = Math.max(0, opts.maxQueuedBytes ?? 4 * (1 << 20));
50
+ this.ws.binaryType = "arraybuffer";
51
+ this.ws.addEventListener("message", this.onMessage);
52
+ this.ws.addEventListener("error", this.onError);
53
+ this.ws.addEventListener("close", this.onClose);
54
+ }
55
+ // readBinary resolves with the next queued binary message.
56
+ readBinary(opts = {}) {
57
+ if (opts.signal?.aborted)
58
+ return Promise.reject(new AbortError("read aborted"));
59
+ if (this.error != null)
60
+ return Promise.reject(this.error);
61
+ const b = this.shiftQueue();
62
+ if (b != null) {
63
+ this.queueBytes -= b.length;
64
+ return Promise.resolve(b);
65
+ }
66
+ return new Promise((resolve, reject) => {
67
+ if (opts.signal?.aborted) {
68
+ reject(new AbortError("read aborted"));
69
+ return;
70
+ }
71
+ if (this.error != null) {
72
+ reject(this.error);
73
+ return;
74
+ }
75
+ const waiter = {
76
+ resolve,
77
+ reject,
78
+ settled: false,
79
+ cleanup: () => { }
80
+ };
81
+ let timeout;
82
+ const onAbort = () => {
83
+ if (waiter.settled)
84
+ return;
85
+ waiter.settled = true;
86
+ waiter.cleanup();
87
+ this.waitersSettled++;
88
+ this.compactWaitersMaybe();
89
+ reject(new AbortError("read aborted"));
90
+ };
91
+ const cleanup = () => {
92
+ if (timeout != null)
93
+ clearTimeout(timeout);
94
+ timeout = undefined;
95
+ opts.signal?.removeEventListener("abort", onAbort);
96
+ };
97
+ waiter.cleanup = cleanup;
98
+ opts.signal?.addEventListener("abort", onAbort);
99
+ const timeoutMs = Math.max(0, opts.timeoutMs ?? 0);
100
+ if (timeoutMs > 0) {
101
+ timeout = setTimeout(() => {
102
+ if (waiter.settled)
103
+ return;
104
+ waiter.settled = true;
105
+ cleanup();
106
+ this.waitersSettled++;
107
+ this.compactWaitersMaybe();
108
+ reject(new TimeoutError("read timeout"));
109
+ }, timeoutMs);
110
+ }
111
+ this.waiters.push(waiter);
112
+ });
113
+ }
114
+ // writeBinary sends a binary frame over the websocket.
115
+ async writeBinary(frame, opts = {}) {
116
+ throwIfAborted(opts.signal, "write aborted");
117
+ if (this.error != null)
118
+ throw this.error;
119
+ this.ws.send(frame);
120
+ }
121
+ // close tears down listeners and rejects pending readers.
122
+ close() {
123
+ if (!this.localCloseRequested) {
124
+ this.localCloseRequested = true;
125
+ if (this.error == null)
126
+ this.observer.onWsClose("local");
127
+ }
128
+ this.fail(new Error("websocket closed"));
129
+ this.ws.removeEventListener("message", this.onMessage);
130
+ this.ws.removeEventListener("error", this.onError);
131
+ this.ws.removeEventListener("close", this.onClose);
132
+ this.queue.length = 0;
133
+ this.queueHead = 0;
134
+ this.queueBytes = 0;
135
+ this.ws.close();
136
+ }
137
+ // handleMessage normalizes browser message payloads into Uint8Array.
138
+ async handleMessage(data) {
139
+ if (this.error != null)
140
+ return;
141
+ if (typeof data === "string") {
142
+ this.fail(new Error("unexpected text frame"), "unexpected_text_frame");
143
+ return;
144
+ }
145
+ if (data instanceof Uint8Array) {
146
+ this.push(data);
147
+ return;
148
+ }
149
+ if (data instanceof ArrayBuffer) {
150
+ if (this.maxQueuedBytes > 0 && this.queueBytes + data.byteLength > this.maxQueuedBytes) {
151
+ this.fail(new Error("ws recv buffer exceeded"), "recv_buffer_exceeded");
152
+ this.localCloseRequested = true;
153
+ this.observer.onWsClose("local");
154
+ this.ws.close();
155
+ return;
156
+ }
157
+ this.push(new Uint8Array(data));
158
+ return;
159
+ }
160
+ if (ArrayBuffer.isView(data)) {
161
+ const view = data;
162
+ if (this.maxQueuedBytes > 0 && this.queueBytes + view.byteLength > this.maxQueuedBytes) {
163
+ this.fail(new Error("ws recv buffer exceeded"), "recv_buffer_exceeded");
164
+ this.localCloseRequested = true;
165
+ this.observer.onWsClose("local");
166
+ this.ws.close();
167
+ return;
168
+ }
169
+ this.push(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
170
+ return;
171
+ }
172
+ if (typeof Blob !== "undefined" && data instanceof Blob) {
173
+ if (this.maxQueuedBytes > 0 && this.queueBytes + data.size > this.maxQueuedBytes) {
174
+ this.fail(new Error("ws recv buffer exceeded"), "recv_buffer_exceeded");
175
+ this.localCloseRequested = true;
176
+ this.observer.onWsClose("local");
177
+ this.ws.close();
178
+ return;
179
+ }
180
+ const ab = await data.arrayBuffer();
181
+ if (this.error != null)
182
+ return;
183
+ this.push(new Uint8Array(ab));
184
+ return;
185
+ }
186
+ this.fail(new Error("unexpected message type"), "unexpected_message_type");
187
+ }
188
+ onMessage = (ev) => {
189
+ const data = ev.data;
190
+ this.messageChain = this.messageChain.then(() => this.handleMessage(data)).catch((err) => {
191
+ const e = err instanceof Error ? err : new Error(String(err));
192
+ this.fail(e, "error");
193
+ });
194
+ };
195
+ onError = () => {
196
+ this.fail(new Error("websocket error"), "error");
197
+ };
198
+ onClose = (ev) => {
199
+ if (!this.localCloseRequested) {
200
+ const code = typeof ev?.code === "number" ? ev.code : undefined;
201
+ const reason = typeof ev?.reason === "string" ? ev.reason : undefined;
202
+ this.observer.onWsClose("peer_or_error", code);
203
+ this.fail(new WsCloseError(code, reason));
204
+ return;
205
+ }
206
+ this.fail(new Error("websocket closed"));
207
+ };
208
+ // push enqueues a frame or delivers it to a waiting reader.
209
+ push(b) {
210
+ const w = this.shiftWaiter();
211
+ if (w != null) {
212
+ if (!w.settled) {
213
+ w.settled = true;
214
+ w.cleanup();
215
+ }
216
+ w.resolve(b);
217
+ return;
218
+ }
219
+ if (this.maxQueuedBytes > 0 && this.queueBytes + b.length > this.maxQueuedBytes) {
220
+ this.fail(new Error("ws recv buffer exceeded"), "recv_buffer_exceeded");
221
+ this.localCloseRequested = true;
222
+ this.observer.onWsClose("local");
223
+ this.ws.close();
224
+ return;
225
+ }
226
+ this.queue.push(b);
227
+ this.queueBytes += b.length;
228
+ }
229
+ shiftQueue() {
230
+ if (this.queueHead >= this.queue.length)
231
+ return undefined;
232
+ const b = this.queue[this.queueHead];
233
+ this.queueHead++;
234
+ // Periodic compaction to release references and keep array bounded.
235
+ if (this.queueHead > 1024 && this.queueHead * 2 > this.queue.length) {
236
+ this.queue.splice(0, this.queueHead);
237
+ this.queueHead = 0;
238
+ }
239
+ return b;
240
+ }
241
+ shiftWaiter() {
242
+ while (this.waitersHead < this.waiters.length) {
243
+ const w = this.waiters[this.waitersHead];
244
+ this.waitersHead++;
245
+ if (w != null && !w.settled) {
246
+ if (this.waitersHead > 1024 && this.waitersHead * 2 > this.waiters.length) {
247
+ this.waiters.splice(0, this.waitersHead);
248
+ this.waitersHead = 0;
249
+ this.waitersSettled = 0;
250
+ }
251
+ return w;
252
+ }
253
+ }
254
+ if (this.waitersHead > 1024 && this.waitersHead * 2 > this.waiters.length) {
255
+ this.waiters.splice(0, this.waitersHead);
256
+ this.waitersHead = 0;
257
+ this.waitersSettled = 0;
258
+ }
259
+ return undefined;
260
+ }
261
+ compactWaitersMaybe() {
262
+ // Timeouts/cancellations can settle waiters without any incoming messages,
263
+ // so we compact based on how many settled entries we're carrying around.
264
+ if (this.waiters.length < 64)
265
+ return;
266
+ if (this.waitersSettled < 32)
267
+ return;
268
+ if (this.waitersSettled * 2 <= this.waiters.length)
269
+ return;
270
+ const next = [];
271
+ for (let i = this.waitersHead; i < this.waiters.length; i++) {
272
+ const w = this.waiters[i];
273
+ if (!w.settled)
274
+ next.push(w);
275
+ }
276
+ this.waiters = next;
277
+ this.waitersHead = 0;
278
+ this.waitersSettled = 0;
279
+ }
280
+ // fail transitions the transport into a permanent error state.
281
+ fail(err, reason) {
282
+ if (this.error != null)
283
+ return;
284
+ this.error = err;
285
+ if (reason != null) {
286
+ this.observer.onWsError(reason);
287
+ }
288
+ const ws = this.waiters;
289
+ const start = this.waitersHead;
290
+ this.waiters = [];
291
+ this.waitersHead = 0;
292
+ for (let i = start; i < ws.length; i++) {
293
+ const w = ws[i];
294
+ if (w == null || w.settled)
295
+ continue;
296
+ w.settled = true;
297
+ w.cleanup();
298
+ w.reject(err);
299
+ }
300
+ }
301
+ }
@@ -0,0 +1,10 @@
1
+ export declare class ByteReader {
2
+ private readonly readChunk;
3
+ private readonly chunks;
4
+ private chunkHead;
5
+ private headOff;
6
+ private buffered;
7
+ constructor(readChunk: () => Promise<Uint8Array | null>);
8
+ readExactly(n: number): Promise<Uint8Array>;
9
+ bufferedBytes(): number;
10
+ }
@@ -0,0 +1,50 @@
1
+ // ByteReader buffers incoming chunks and supports exact reads.
2
+ export class ByteReader {
3
+ readChunk;
4
+ chunks = [];
5
+ chunkHead = 0;
6
+ headOff = 0;
7
+ buffered = 0;
8
+ constructor(readChunk) {
9
+ this.readChunk = readChunk;
10
+ }
11
+ // readExactly reads n bytes or throws on EOF.
12
+ async readExactly(n) {
13
+ if (n < 0)
14
+ throw new Error("invalid length");
15
+ while (this.buffered < n) {
16
+ const chunk = await this.readChunk();
17
+ if (chunk == null)
18
+ throw new Error("eof");
19
+ if (chunk.length === 0)
20
+ continue;
21
+ this.chunks.push(chunk);
22
+ this.buffered += chunk.length;
23
+ }
24
+ const out = new Uint8Array(n);
25
+ let outOff = 0;
26
+ while (outOff < n) {
27
+ const head = this.chunks[this.chunkHead];
28
+ const avail = head.length - this.headOff;
29
+ const need = n - outOff;
30
+ const take = Math.min(avail, need);
31
+ out.set(head.subarray(this.headOff, this.headOff + take), outOff);
32
+ outOff += take;
33
+ this.headOff += take;
34
+ this.buffered -= take;
35
+ if (this.headOff === head.length) {
36
+ this.chunkHead++;
37
+ this.headOff = 0;
38
+ if (this.chunkHead > 1024 && this.chunkHead * 2 > this.chunks.length) {
39
+ this.chunks.splice(0, this.chunkHead);
40
+ this.chunkHead = 0;
41
+ }
42
+ }
43
+ }
44
+ return out;
45
+ }
46
+ // bufferedBytes returns the number of bytes currently buffered.
47
+ bufferedBytes() {
48
+ return this.buffered;
49
+ }
50
+ }
@@ -0,0 +1,10 @@
1
+ export declare const YAMUX_VERSION: 0;
2
+ export declare const TYPE_DATA: 0;
3
+ export declare const TYPE_WINDOW_UPDATE: 1;
4
+ export declare const TYPE_PING: 2;
5
+ export declare const TYPE_GO_AWAY: 3;
6
+ export declare const FLAG_SYN: 1;
7
+ export declare const FLAG_ACK: 2;
8
+ export declare const FLAG_FIN: 4;
9
+ export declare const FLAG_RST: 8;
10
+ export declare const DEFAULT_MAX_STREAM_WINDOW: number;
@@ -0,0 +1,14 @@
1
+ // YAMUX protocol version.
2
+ export const YAMUX_VERSION = 0;
3
+ // Frame types.
4
+ export const TYPE_DATA = 0;
5
+ export const TYPE_WINDOW_UPDATE = 1;
6
+ export const TYPE_PING = 2;
7
+ export const TYPE_GO_AWAY = 3;
8
+ // Frame flags.
9
+ export const FLAG_SYN = 1;
10
+ export const FLAG_ACK = 2;
11
+ export const FLAG_FIN = 4;
12
+ export const FLAG_RST = 8;
13
+ // Default flow-control window for streams.
14
+ export const DEFAULT_MAX_STREAM_WINDOW = 256 * 1024;
@@ -0,0 +1,17 @@
1
+ export declare const HEADER_LEN = 12;
2
+ export type YamuxHeader = Readonly<{
3
+ /** Protocol version byte. */
4
+ version: number;
5
+ /** Frame type (data, window update, ping, go away). */
6
+ type: number;
7
+ /** 16-bit flags field (ACK/SYN/FIN/RST). */
8
+ flags: number;
9
+ /** Stream identifier (0 reserved for session-level frames). */
10
+ streamId: number;
11
+ /** Frame payload length in bytes. */
12
+ length: number;
13
+ }>;
14
+ export declare function encodeHeader(h: Omit<YamuxHeader, "version"> & {
15
+ version?: number;
16
+ }): Uint8Array;
17
+ export declare function decodeHeader(buf: Uint8Array, off: number): YamuxHeader;
@@ -0,0 +1,26 @@
1
+ import { readU32be, u32be } from "../utils/bin.js";
2
+ import { YAMUX_VERSION } from "./constants.js";
3
+ // YAMUX header length in bytes.
4
+ export const HEADER_LEN = 12;
5
+ // encodeHeader serializes a header with an optional version override.
6
+ export function encodeHeader(h) {
7
+ const out = new Uint8Array(HEADER_LEN);
8
+ out[0] = (h.version ?? YAMUX_VERSION) & 0xff;
9
+ out[1] = h.type & 0xff;
10
+ out[2] = (h.flags >>> 8) & 0xff;
11
+ out[3] = h.flags & 0xff;
12
+ out.set(u32be(h.streamId >>> 0), 4);
13
+ out.set(u32be(h.length >>> 0), 8);
14
+ return out;
15
+ }
16
+ // decodeHeader parses a header from a byte buffer at the given offset.
17
+ export function decodeHeader(buf, off) {
18
+ if (buf.length - off < HEADER_LEN)
19
+ throw new Error("header too short");
20
+ const version = buf[off];
21
+ const type = buf[off + 1];
22
+ const flags = ((buf[off + 2] << 8) | buf[off + 3]) >>> 0;
23
+ const streamId = readU32be(buf, off + 4);
24
+ const length = readU32be(buf, off + 8);
25
+ return { version, type, flags, streamId, length };
26
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./constants.js";
2
+ export * from "./byteReader.js";
3
+ export * from "./header.js";
4
+ export * from "./session.js";
5
+ export * from "./stream.js";
@@ -0,0 +1,5 @@
1
+ export * from "./constants.js";
2
+ export * from "./byteReader.js";
3
+ export * from "./header.js";
4
+ export * from "./session.js";
5
+ export * from "./stream.js";
@@ -0,0 +1,44 @@
1
+ import { YamuxStream } from "./stream.js";
2
+ export type ByteDuplex = {
3
+ /** Reads the next chunk from the underlying connection. */
4
+ read(): Promise<Uint8Array>;
5
+ /** Writes a chunk to the underlying connection. */
6
+ write(chunk: Uint8Array): Promise<void>;
7
+ /** Closes the underlying connection. */
8
+ close(): void;
9
+ };
10
+ export type YamuxSessionOptions = Readonly<{
11
+ /** True when acting as the client side (odd stream IDs). */
12
+ client: boolean;
13
+ /** Optional callback for newly accepted inbound streams. */
14
+ onIncomingStream?: (s: YamuxStream) => void;
15
+ /** Maximum frame payload bytes accepted per stream frame. */
16
+ maxFrameBytes?: number;
17
+ }>;
18
+ export declare class YamuxSession {
19
+ private readonly conn;
20
+ private readonly reader;
21
+ private readonly streams;
22
+ private readonly onIncomingStream;
23
+ private readonly maxFrameBytes;
24
+ private readonly client;
25
+ private nextStreamId;
26
+ private closed;
27
+ private readonly sendWindowWaiters;
28
+ constructor(conn: ByteDuplex, opts: YamuxSessionOptions);
29
+ openStream(): Promise<YamuxStream>;
30
+ getStream(id: number): YamuxStream | undefined;
31
+ writeRaw(chunk: Uint8Array): Promise<void>;
32
+ sendRst(id: number): Promise<void>;
33
+ notifySendWindow(streamId: number): void;
34
+ waitForSendWindow(streamId: number): Promise<void>;
35
+ onStreamEstablished(_streamId: number): void;
36
+ onStreamClosed(streamId: number): void;
37
+ close(): void;
38
+ private wakeSendWindowWaiters;
39
+ private readLoop;
40
+ private handlePing;
41
+ private handleDataFrame;
42
+ private handleWindowUpdateFrame;
43
+ private isInboundStreamIdValid;
44
+ }