@floegence/flowersec-core 2.5.3 → 3.0.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.
Files changed (119) hide show
  1. package/README.md +46 -18
  2. package/dist/browser/connectSessionV3.d.ts +13 -0
  3. package/dist/browser/connectSessionV3.js +75 -0
  4. package/dist/browser/index.d.ts +3 -2
  5. package/dist/browser/index.js +2 -1
  6. package/dist/browser/v2.d.ts +3 -0
  7. package/dist/browser/v2.js +2 -0
  8. package/dist/facade.d.ts +15 -8
  9. package/dist/facade.js +5 -4
  10. package/dist/framing/jsonframe.d.ts +1 -1
  11. package/dist/framing/jsonframe.js +3 -1
  12. package/dist/interop/proxyServerPeer.js +1 -1
  13. package/dist/interop/serverParityPeer.js +8 -1
  14. package/dist/node/acceptor.d.ts +10 -0
  15. package/dist/node/acceptor.js +53 -7
  16. package/dist/node/acceptorV3.d.ts +57 -0
  17. package/dist/node/acceptorV3.js +227 -0
  18. package/dist/node/connectSessionV3.d.ts +21 -0
  19. package/dist/node/connectSessionV3.js +73 -0
  20. package/dist/node/index.d.ts +15 -10
  21. package/dist/node/index.js +6 -5
  22. package/dist/node/nativeTransportAddon.d.ts +39 -1
  23. package/dist/node/nativeTransportAddon.js +29 -5
  24. package/dist/node/rawQuicAdapterV3.d.ts +10 -0
  25. package/dist/node/rawQuicAdapterV3.js +76 -0
  26. package/dist/node/rawQuicServerV3.d.ts +24 -0
  27. package/dist/node/rawQuicServerV3.js +34 -0
  28. package/dist/node/runtimeAuthorizationV3.d.ts +29 -0
  29. package/dist/node/runtimeAuthorizationV3.js +126 -0
  30. package/dist/node/tunnelRuntimeV3.d.ts +57 -0
  31. package/dist/node/tunnelRuntimeV3.js +857 -0
  32. package/dist/node/v2.d.ts +11 -0
  33. package/dist/node/v2.js +6 -0
  34. package/dist/node/webSocketServerV3.d.ts +27 -0
  35. package/dist/node/webSocketServerV3.js +219 -0
  36. package/dist/proxy/runtime.js +39 -6
  37. package/dist/proxy/serviceWorker.d.ts +2 -0
  38. package/dist/proxy/serviceWorker.js +63 -7
  39. package/dist/proxy/windowBridge.d.ts +1 -0
  40. package/dist/proxy/windowBridge.js +93 -33
  41. package/dist/proxy/wsPatch.d.ts +1 -0
  42. package/dist/proxy/wsPatch.js +39 -6
  43. package/dist/public/streamHandlers.d.ts +3 -0
  44. package/dist/public/streamHandlers.js +6 -1
  45. package/dist/rpc/client.js +9 -7
  46. package/dist/rpc/server.js +44 -20
  47. package/dist/rpc/validate.d.ts +1 -0
  48. package/dist/rpc/validate.js +18 -6
  49. package/dist/v2/index.d.ts +14 -0
  50. package/dist/v2/index.js +7 -0
  51. package/dist/v2/protocol.js +23 -32
  52. package/dist/v2/publicSession.js +4 -0
  53. package/dist/v2/session.d.ts +5 -0
  54. package/dist/v2/session.js +94 -35
  55. package/dist/v3/admissionError.d.ts +4 -0
  56. package/dist/v3/admissionError.js +8 -0
  57. package/dist/v3/artifact.d.ts +145 -0
  58. package/dist/v3/artifact.js +1162 -0
  59. package/dist/v3/artifactLease.d.ts +17 -0
  60. package/dist/v3/artifactLease.js +109 -0
  61. package/dist/v3/browserRuntime.d.ts +37 -0
  62. package/dist/v3/browserRuntime.js +197 -0
  63. package/dist/v3/browserSessionRuntime.d.ts +2 -0
  64. package/dist/v3/browserSessionRuntime.js +21 -0
  65. package/dist/v3/capability.d.ts +32 -0
  66. package/dist/v3/capability.js +345 -0
  67. package/dist/v3/carrier.d.ts +84 -0
  68. package/dist/v3/carrier.js +411 -0
  69. package/dist/v3/connectionController.d.ts +87 -0
  70. package/dist/v3/connectionController.js +805 -0
  71. package/dist/v3/contract.d.ts +65 -0
  72. package/dist/v3/contract.js +3 -0
  73. package/dist/v3/controller.d.ts +43 -0
  74. package/dist/v3/controller.js +303 -0
  75. package/dist/v3/handshake.d.ts +81 -0
  76. package/dist/v3/handshake.js +471 -0
  77. package/dist/v3/jcs.d.ts +6 -0
  78. package/dist/v3/jcs.js +69 -0
  79. package/dist/v3/jsonPreflight.d.ts +1 -0
  80. package/dist/v3/jsonPreflight.js +251 -0
  81. package/dist/v3/nodeRuntime.d.ts +40 -0
  82. package/dist/v3/nodeRuntime.js +315 -0
  83. package/dist/v3/nodeSessionRuntime.d.ts +2 -0
  84. package/dist/v3/nodeSessionRuntime.js +13 -0
  85. package/dist/v3/protocol.d.ts +99 -0
  86. package/dist/v3/protocol.js +773 -0
  87. package/dist/v3/publicApi.d.ts +12 -0
  88. package/dist/v3/publicApi.js +46 -0
  89. package/dist/v3/publicSession.d.ts +1 -0
  90. package/dist/v3/publicSession.js +281 -0
  91. package/dist/v3/retryDisposition.d.ts +16 -0
  92. package/dist/v3/retryDisposition.js +30 -0
  93. package/dist/v3/runtimeAdapters.d.ts +9 -0
  94. package/dist/v3/runtimeAdapters.js +109 -0
  95. package/dist/v3/security.d.ts +34 -0
  96. package/dist/v3/security.js +100 -0
  97. package/dist/v3/serverAdmission.d.ts +38 -0
  98. package/dist/v3/serverAdmission.js +223 -0
  99. package/dist/v3/session.d.ts +318 -0
  100. package/dist/v3/session.js +2289 -0
  101. package/dist/v3/sessionConnector.d.ts +37 -0
  102. package/dist/v3/sessionConnector.js +440 -0
  103. package/dist/v3/streamLifetimeLedger.d.ts +31 -0
  104. package/dist/v3/streamLifetimeLedger.js +131 -0
  105. package/dist/v3/streamMetadata.d.ts +1 -0
  106. package/dist/v3/streamMetadata.js +7 -0
  107. package/dist/v3/transportConstants.d.ts +56 -0
  108. package/dist/v3/transportConstants.js +59 -0
  109. package/dist/v3/unreliableMessage.d.ts +44 -0
  110. package/dist/v3/unreliableMessage.js +284 -0
  111. package/dist/v3/webSocketCarrier.d.ts +31 -0
  112. package/dist/v3/webSocketCarrier.js +370 -0
  113. package/dist/v3/webTransportCarrier.d.ts +27 -0
  114. package/dist/v3/webTransportCarrier.js +534 -0
  115. package/dist/yamux/stream.d.ts +3 -0
  116. package/dist/yamux/stream.js +17 -4
  117. package/package.json +4 -4
  118. package/sbom/cyclonedx.json +6 -6
  119. package/sbom/spdx.json +11 -11
@@ -0,0 +1,534 @@
1
+ import { CarrierError, } from "./carrier.js";
2
+ export function adaptWebTransportCarrierSessionV3(native, options) {
3
+ requireWebTransportSessionSurfaceV3(native);
4
+ return new WebTransportCarrierSessionAdapter(native, options);
5
+ }
6
+ export function hasWebTransportConstructorSurfaceV3(value) {
7
+ if (typeof value !== "function")
8
+ return false;
9
+ const prototype = value.prototype;
10
+ return typeof prototype === "object" && prototype !== null &&
11
+ typeof prototype.createBidirectionalStream === "function" &&
12
+ typeof prototype.close === "function" &&
13
+ "incomingBidirectionalStreams" in prototype &&
14
+ "datagrams" in prototype &&
15
+ "ready" in prototype &&
16
+ "closed" in prototype;
17
+ }
18
+ function requireWebTransportSessionSurfaceV3(value) {
19
+ const missing = [];
20
+ if (typeof value.createBidirectionalStream !== "function")
21
+ missing.push("outgoing bidirectional streams");
22
+ if (typeof value.incomingBidirectionalStreams?.getReader !== "function")
23
+ missing.push("incoming bidirectional streams");
24
+ if (typeof value.datagrams?.readable?.getReader !== "function")
25
+ missing.push("incoming DATAGRAMs");
26
+ if (typeof value.datagrams?.writable?.getWriter !== "function")
27
+ missing.push("outgoing DATAGRAMs");
28
+ if (typeof value.close !== "function")
29
+ missing.push("close");
30
+ if (typeof value.closed?.then !== "function")
31
+ missing.push("termination");
32
+ if (missing.length !== 0) {
33
+ throw new TypeError(`WebTransport runtime lacks required ${missing.join(", ")}`);
34
+ }
35
+ }
36
+ class WebTransportCarrierSessionAdapter {
37
+ native;
38
+ kind = "webtransport";
39
+ path;
40
+ inboundBidirectionalStreamCapacity;
41
+ unreliableDatagrams;
42
+ incoming;
43
+ incomingQueue = new IncomingWebTransportStreamQueue();
44
+ streams = new Set();
45
+ datagramsAdapter;
46
+ closed = false;
47
+ terminalError;
48
+ closePromise;
49
+ activeIncomingStreams = 0;
50
+ constructor(native, options) {
51
+ this.native = native;
52
+ if (!Number.isInteger(options.inboundBidirectionalStreamCapacity) || options.inboundBidirectionalStreamCapacity < 3) {
53
+ throw new RangeError("WebTransport carrier stream capacity must be at least 3");
54
+ }
55
+ this.path = options.path;
56
+ this.inboundBidirectionalStreamCapacity = options.inboundBidirectionalStreamCapacity;
57
+ this.incoming = native.incomingBidirectionalStreams.getReader();
58
+ this.datagramsAdapter = new WebTransportDatagramAdapter(native.datagrams, options.datagramMaxSize);
59
+ this.unreliableDatagrams = this.datagramsAdapter;
60
+ void this.pumpIncoming();
61
+ void native.closed.then(() => { this.terminate(new CarrierError("closed", "WebTransport carrier is closed")); }, (error) => {
62
+ this.terminate(carrierFailure("WebTransport closed with an error", error));
63
+ });
64
+ }
65
+ async openStream(options = {}) {
66
+ this.assertOpen();
67
+ if (options.signal?.aborted === true)
68
+ throw abortedCarrierError(options.signal.reason);
69
+ const pending = this.native.createBidirectionalStream();
70
+ let stream;
71
+ try {
72
+ stream = await carrierCall(raceAbort(pending, options.signal), "WebTransport failed to open a stream");
73
+ }
74
+ catch (error) {
75
+ if (isAborted(options.signal)) {
76
+ void pending.then((late) => abortNativeStream(late, abortedCarrierError()), () => undefined);
77
+ }
78
+ throw error;
79
+ }
80
+ try {
81
+ this.assertOpen();
82
+ }
83
+ catch (error) {
84
+ abortNativeStream(stream, asError(error));
85
+ throw error;
86
+ }
87
+ return this.track(stream);
88
+ }
89
+ async acceptStream(options = {}) {
90
+ this.assertOpen();
91
+ return await this.incomingQueue.shift(options.signal);
92
+ }
93
+ async waitTermination() {
94
+ try {
95
+ await this.native.closed;
96
+ }
97
+ catch (error) {
98
+ throw carrierFailure("WebTransport terminated with an error", error);
99
+ }
100
+ }
101
+ close() {
102
+ this.closePromise ??= this.closeOnce({ closeCode: 0, reason: "" });
103
+ return this.closePromise;
104
+ }
105
+ abort(error = { code: 6, reason: "carrier aborted" }) {
106
+ if (this.closed)
107
+ return;
108
+ this.terminate(new CarrierError("aborted", "WebTransport carrier aborted"));
109
+ try {
110
+ this.native.close({ closeCode: error.code, reason: error.reason });
111
+ }
112
+ catch (cause) {
113
+ this.terminalError = carrierFailure("WebTransport abort failed", cause);
114
+ }
115
+ }
116
+ async closeOnce(info) {
117
+ if (this.closed)
118
+ return;
119
+ this.terminate(new CarrierError("closed", "WebTransport carrier is closed"));
120
+ try {
121
+ this.native.close(info);
122
+ await this.native.closed;
123
+ }
124
+ catch (error) {
125
+ throw carrierFailure("WebTransport close failed", error);
126
+ }
127
+ }
128
+ assertOpen() {
129
+ if (this.terminalError !== undefined)
130
+ throw this.terminalError;
131
+ if (this.closed)
132
+ throw new CarrierError("closed", "WebTransport carrier is closed");
133
+ }
134
+ track(native, incoming = false) {
135
+ if (incoming)
136
+ this.activeIncomingStreams += 1;
137
+ let stream;
138
+ stream = new WebTransportCarrierStreamAdapter(native, () => {
139
+ this.streams.delete(stream);
140
+ if (incoming)
141
+ this.activeIncomingStreams = Math.max(0, this.activeIncomingStreams - 1);
142
+ });
143
+ this.streams.add(stream);
144
+ return stream;
145
+ }
146
+ async pumpIncoming() {
147
+ try {
148
+ while (!this.closed) {
149
+ const result = await this.incoming.read();
150
+ if (result.done || result.value === undefined) {
151
+ this.terminate(new CarrierError("closed", "WebTransport incoming streams closed"));
152
+ return;
153
+ }
154
+ if (this.activeIncomingStreams >= this.inboundBidirectionalStreamCapacity) {
155
+ abortNativeStream(result.value, new CarrierError("reset", "WebTransport inbound stream capacity exceeded"));
156
+ continue;
157
+ }
158
+ this.incomingQueue.push(this.track(result.value, true));
159
+ }
160
+ }
161
+ catch (error) {
162
+ if (!this.closed)
163
+ this.terminate(carrierFailure("WebTransport stream accept failed", error));
164
+ }
165
+ }
166
+ terminate(error) {
167
+ if (this.closed)
168
+ return;
169
+ this.closed = true;
170
+ if (error.code !== "closed")
171
+ this.terminalError = error;
172
+ this.incomingQueue.fail(error);
173
+ for (const stream of [...this.streams])
174
+ stream.abort(error);
175
+ this.streams.clear();
176
+ this.datagramsAdapter.abort(error);
177
+ }
178
+ }
179
+ class WebTransportCarrierStreamAdapter {
180
+ onRelease;
181
+ reader;
182
+ writer;
183
+ readClosed = false;
184
+ readStopError;
185
+ writeClosed = false;
186
+ terminalError;
187
+ closeWritePromise;
188
+ stopSendingPromise;
189
+ resetPromise;
190
+ released = false;
191
+ constructor(stream, onRelease) {
192
+ this.onRelease = onRelease;
193
+ this.reader = stream.readable.getReader();
194
+ this.writer = stream.writable.getWriter();
195
+ }
196
+ async read(options = {}) {
197
+ this.assertReadable();
198
+ if (this.readClosed)
199
+ return null;
200
+ const result = await carrierCall(raceAbort(this.reader.read(), options.signal), "WebTransport stream read failed", "reset");
201
+ if (result.done || result.value === undefined) {
202
+ this.readClosed = true;
203
+ this.releaseIfComplete();
204
+ return null;
205
+ }
206
+ return result.value.slice();
207
+ }
208
+ async write(data) {
209
+ this.assertNotTerminal();
210
+ if (this.writeClosed)
211
+ throw new CarrierError("write_closed", "WebTransport stream write side is closed");
212
+ await carrierCall(this.writer.write(data.slice()), "WebTransport stream write failed", "reset");
213
+ return data.byteLength;
214
+ }
215
+ closeWrite() {
216
+ if (this.closeWritePromise !== undefined)
217
+ return this.closeWritePromise;
218
+ this.assertNotTerminal();
219
+ this.writeClosed = true;
220
+ this.closeWritePromise = carrierCall(this.writer.close(), "WebTransport stream FIN failed", "reset")
221
+ .then(() => { this.releaseIfComplete(); });
222
+ return this.closeWritePromise;
223
+ }
224
+ stopSending() {
225
+ if (this.stopSendingPromise !== undefined)
226
+ return this.stopSendingPromise;
227
+ this.assertNotTerminal();
228
+ const stopped = new CarrierError("stop_sending", "WebTransport stream receive side stopped");
229
+ this.readClosed = true;
230
+ this.readStopError = stopped;
231
+ this.stopSendingPromise ??= carrierCall(this.reader.cancel(stopped), "WebTransport STOP_SENDING failed", "stop_sending").then(() => { this.releaseIfComplete(); });
232
+ return this.stopSendingPromise;
233
+ }
234
+ reset() {
235
+ this.resetPromise ??= this.resetOnce();
236
+ return this.resetPromise;
237
+ }
238
+ abort(error = new CarrierError("aborted", "WebTransport stream aborted")) {
239
+ if (this.terminalError !== undefined)
240
+ return;
241
+ this.terminalError = error instanceof CarrierError
242
+ ? error
243
+ : new CarrierError("aborted", "WebTransport stream aborted", error);
244
+ this.readClosed = true;
245
+ this.writeClosed = true;
246
+ void this.writer.abort(this.terminalError).catch(() => undefined);
247
+ void this.reader.cancel(this.terminalError).catch(() => undefined);
248
+ this.release();
249
+ }
250
+ async resetOnce() {
251
+ if (this.terminalError !== undefined)
252
+ return;
253
+ this.writeClosed = true;
254
+ this.readClosed = true;
255
+ const error = new CarrierError("reset", "WebTransport stream reset");
256
+ this.terminalError = error;
257
+ try {
258
+ await Promise.allSettled([this.writer.abort(error), this.reader.cancel(error)]);
259
+ }
260
+ finally {
261
+ this.release();
262
+ }
263
+ }
264
+ assertReadable() {
265
+ this.assertNotTerminal();
266
+ if (this.readStopError !== undefined)
267
+ throw this.readStopError;
268
+ }
269
+ assertNotTerminal() {
270
+ if (this.terminalError !== undefined)
271
+ throw this.terminalError;
272
+ }
273
+ releaseIfComplete() {
274
+ if (this.readClosed && this.writeClosed)
275
+ this.release();
276
+ }
277
+ release() {
278
+ if (this.released)
279
+ return;
280
+ this.released = true;
281
+ this.onRelease();
282
+ }
283
+ }
284
+ class WebTransportDatagramAdapter {
285
+ maxDatagramSize;
286
+ readable;
287
+ writable;
288
+ incoming = new IncomingDatagramQueue();
289
+ terminalError;
290
+ constructor(native, configuredMaxSize) {
291
+ const maxDatagramSize = configuredMaxSize ?? native.maxDatagramSize;
292
+ if (!Number.isInteger(maxDatagramSize) || maxDatagramSize < 1) {
293
+ throw new TypeError("WebTransport runtime did not provide a valid DATAGRAM size");
294
+ }
295
+ this.maxDatagramSize = maxDatagramSize;
296
+ this.readable = native.readable.getReader();
297
+ this.writable = native.writable.getWriter();
298
+ void this.pumpIncoming();
299
+ }
300
+ async send(data, options = {}) {
301
+ this.assertOpen();
302
+ if (options.signal?.aborted === true)
303
+ throw abortedCarrierError(options.signal.reason);
304
+ if (options.expiresAt !== undefined && options.expiresAt <= Date.now())
305
+ return "dropped_expired";
306
+ if (data.byteLength < 1 || data.byteLength > this.maxDatagramSize)
307
+ return "dropped_carrier";
308
+ try {
309
+ await raceAbort(this.writable.write(data.slice()), options.signal);
310
+ return "accepted";
311
+ }
312
+ catch (error) {
313
+ if (isAborted(options.signal))
314
+ throw abortedCarrierError(error);
315
+ if (this.terminalError !== undefined)
316
+ throw this.terminalError;
317
+ return "dropped_carrier";
318
+ }
319
+ }
320
+ async receive(options = {}) {
321
+ this.assertOpen();
322
+ return await this.incoming.shift(options.signal);
323
+ }
324
+ abort(error) {
325
+ if (this.terminalError !== undefined)
326
+ return;
327
+ this.terminalError = error;
328
+ this.incoming.fail(error);
329
+ void this.writable.abort(error).catch(() => undefined);
330
+ void this.readable.cancel(error).catch(() => undefined);
331
+ }
332
+ assertOpen() {
333
+ if (this.terminalError !== undefined)
334
+ throw this.terminalError;
335
+ }
336
+ async pumpIncoming() {
337
+ try {
338
+ while (this.terminalError === undefined) {
339
+ const result = await this.readable.read();
340
+ if (result.done || result.value === undefined) {
341
+ this.abort(new CarrierError("closed", "WebTransport datagrams closed"));
342
+ return;
343
+ }
344
+ if (result.value.byteLength < 1 || result.value.byteLength > this.maxDatagramSize)
345
+ continue;
346
+ this.incoming.push(result.value.slice());
347
+ }
348
+ }
349
+ catch (error) {
350
+ if (this.terminalError === undefined) {
351
+ this.abort(carrierFailure("WebTransport datagram receive failed", error));
352
+ }
353
+ }
354
+ }
355
+ }
356
+ class IncomingWebTransportStreamQueue {
357
+ values = [];
358
+ waiters = new Set();
359
+ terminalError;
360
+ push(stream) {
361
+ if (this.terminalError !== undefined) {
362
+ stream.abort(this.terminalError);
363
+ return;
364
+ }
365
+ const waiter = this.waiters.values().next().value;
366
+ if (waiter === undefined)
367
+ this.values.push(stream);
368
+ else
369
+ waiter.deliver(stream);
370
+ }
371
+ shift(signal) {
372
+ if (signal?.aborted === true)
373
+ return Promise.reject(abortedCarrierError(signal.reason));
374
+ if (this.terminalError !== undefined)
375
+ return Promise.reject(this.terminalError);
376
+ const value = this.values.shift();
377
+ if (value !== undefined)
378
+ return Promise.resolve(value);
379
+ return new Promise((resolve, reject) => {
380
+ let settled = false;
381
+ const cleanup = () => {
382
+ this.waiters.delete(waiter);
383
+ signal?.removeEventListener("abort", abort);
384
+ };
385
+ const waiter = {
386
+ deliver: (stream) => {
387
+ if (settled)
388
+ return;
389
+ settled = true;
390
+ cleanup();
391
+ resolve(stream);
392
+ },
393
+ fail: (error) => {
394
+ if (settled)
395
+ return;
396
+ settled = true;
397
+ cleanup();
398
+ reject(error);
399
+ },
400
+ };
401
+ const abort = () => waiter.fail(abortedCarrierError(signal?.reason));
402
+ this.waiters.add(waiter);
403
+ signal?.addEventListener("abort", abort, { once: true });
404
+ if (signal?.aborted === true)
405
+ abort();
406
+ });
407
+ }
408
+ fail(error) {
409
+ if (this.terminalError !== undefined)
410
+ return;
411
+ this.terminalError = error;
412
+ for (const stream of this.values)
413
+ stream.abort(error);
414
+ this.values.length = 0;
415
+ for (const waiter of [...this.waiters])
416
+ waiter.fail(error);
417
+ }
418
+ }
419
+ class IncomingDatagramQueue {
420
+ static MAX_VALUES = 256;
421
+ static MAX_BYTES = 4 * 1024 * 1024;
422
+ values = [];
423
+ bufferedBytes = 0;
424
+ waiters = new Set();
425
+ terminalError;
426
+ push(value) {
427
+ if (this.terminalError !== undefined)
428
+ return;
429
+ const waiter = this.waiters.values().next().value;
430
+ if (waiter === undefined) {
431
+ // DATAGRAMs are lossy by contract. Drop an incoming value once the
432
+ // bounded queue budget is exhausted, while keeping the native reader
433
+ // drained so an unconsumed peer cannot grow memory without bound.
434
+ if (this.values.length >= IncomingDatagramQueue.MAX_VALUES ||
435
+ this.bufferedBytes + value.byteLength > IncomingDatagramQueue.MAX_BYTES)
436
+ return;
437
+ this.values.push(value);
438
+ this.bufferedBytes += value.byteLength;
439
+ }
440
+ else
441
+ waiter.deliver(value);
442
+ }
443
+ shift(signal) {
444
+ if (signal?.aborted === true)
445
+ return Promise.reject(abortedCarrierError(signal.reason));
446
+ if (this.terminalError !== undefined)
447
+ return Promise.reject(this.terminalError);
448
+ const value = this.values.shift();
449
+ if (value !== undefined) {
450
+ this.bufferedBytes -= value.byteLength;
451
+ return Promise.resolve(value);
452
+ }
453
+ return new Promise((resolve, reject) => {
454
+ let settled = false;
455
+ const cleanup = () => {
456
+ this.waiters.delete(waiter);
457
+ signal?.removeEventListener("abort", abort);
458
+ };
459
+ const waiter = {
460
+ deliver: (next) => {
461
+ if (settled)
462
+ return;
463
+ settled = true;
464
+ cleanup();
465
+ resolve(next);
466
+ },
467
+ fail: (error) => {
468
+ if (settled)
469
+ return;
470
+ settled = true;
471
+ cleanup();
472
+ reject(error);
473
+ },
474
+ };
475
+ const abort = () => waiter.fail(abortedCarrierError(signal?.reason));
476
+ this.waiters.add(waiter);
477
+ signal?.addEventListener("abort", abort, { once: true });
478
+ if (signal?.aborted === true)
479
+ abort();
480
+ });
481
+ }
482
+ fail(error) {
483
+ if (this.terminalError !== undefined)
484
+ return;
485
+ this.terminalError = error;
486
+ this.values.length = 0;
487
+ this.bufferedBytes = 0;
488
+ for (const waiter of [...this.waiters])
489
+ waiter.fail(error);
490
+ }
491
+ }
492
+ function abortNativeStream(stream, error) {
493
+ const reader = stream.readable.getReader();
494
+ const writer = stream.writable.getWriter();
495
+ void reader.cancel(error).catch(() => undefined);
496
+ void writer.abort(error).catch(() => undefined);
497
+ }
498
+ function abortedCarrierError(cause) {
499
+ return new CarrierError("aborted", "WebTransport operation aborted", cause);
500
+ }
501
+ function isAborted(signal) {
502
+ return signal?.aborted === true;
503
+ }
504
+ async function raceAbort(promise, signal) {
505
+ if (signal === undefined)
506
+ return await promise;
507
+ if (signal.aborted)
508
+ throw abortedCarrierError(signal.reason);
509
+ return await new Promise((resolve, reject) => {
510
+ const abort = () => reject(abortedCarrierError(signal.reason));
511
+ signal.addEventListener("abort", abort, { once: true });
512
+ void promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
513
+ });
514
+ }
515
+ async function carrierCall(promise, message, code = "closed") {
516
+ try {
517
+ return await promise;
518
+ }
519
+ catch (error) {
520
+ if (error instanceof CarrierError)
521
+ throw error;
522
+ if (error instanceof DOMException && error.name === "AbortError" ||
523
+ error instanceof Error && error.name === "AbortError") {
524
+ throw new CarrierError("aborted", message, error);
525
+ }
526
+ throw new CarrierError(code, message, error);
527
+ }
528
+ }
529
+ function carrierFailure(message, cause) {
530
+ return cause instanceof CarrierError ? cause : new CarrierError("closed", message, cause);
531
+ }
532
+ function asError(error) {
533
+ return error instanceof Error ? error : new Error(String(error));
534
+ }
@@ -12,6 +12,8 @@ export declare class YamuxStream {
12
12
  private readWaiters;
13
13
  private error;
14
14
  private resetTask;
15
+ private closeTask;
16
+ private closeRequested;
15
17
  private writeChain;
16
18
  private writeQueueBytes;
17
19
  private finalized;
@@ -23,6 +25,7 @@ export declare class YamuxStream {
23
25
  write(data: Uint8Array): Promise<void>;
24
26
  private writeSerial;
25
27
  close(): Promise<void>;
28
+ private closeSerial;
26
29
  reset(err?: Error): Promise<void>;
27
30
  abort(err?: Error): void;
28
31
  private fail;
@@ -24,6 +24,8 @@ export class YamuxStream {
24
24
  // Terminal error (reset/overflow) for the stream.
25
25
  error = null;
26
26
  resetTask;
27
+ closeTask;
28
+ closeRequested = false;
27
29
  writeChain = Promise.resolve();
28
30
  writeQueueBytes = 0;
29
31
  finalized = false;
@@ -89,6 +91,8 @@ export class YamuxStream {
89
91
  }
90
92
  // write sends DATA frames, respecting the send window.
91
93
  async write(data) {
94
+ if (this.closeRequested)
95
+ throw new Error("stream closed");
92
96
  this.ensureWritable();
93
97
  const byteCount = data.byteLength;
94
98
  const nextQueueBytes = this.writeQueueBytes + byteCount;
@@ -129,10 +133,19 @@ export class YamuxStream {
129
133
  }
130
134
  }
131
135
  // close sends FIN and transitions to local close.
132
- async close() {
133
- if (this.state === "closed")
134
- return;
135
- if (this.state === "reset")
136
+ close() {
137
+ if (this.closeTask !== undefined)
138
+ return this.closeTask;
139
+ if (this.state === "closed" || this.state === "reset" || this.state === "localClose")
140
+ return Promise.resolve();
141
+ this.closeRequested = true;
142
+ const task = this.writeChain.then(async () => await this.closeSerial());
143
+ this.writeChain = task.catch(() => undefined);
144
+ this.closeTask = task;
145
+ return task;
146
+ }
147
+ async closeSerial() {
148
+ if (this.state === "closed" || this.state === "reset" || this.state === "localClose")
136
149
  return;
137
150
  const wasRemoteClose = this.state === "remoteClose";
138
151
  const flags = this.sendFlags() | FLAG_FIN;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floegence/flowersec-core",
3
- "version": "2.5.3",
3
+ "version": "3.0.0",
4
4
  "description": "Flowersec core TypeScript library for carrier-neutral encrypted sessions and multiplexed streams.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -63,7 +63,7 @@
63
63
  "ensure:browser": "node ./scripts/ensure-playwright-browsers.mjs chromium",
64
64
  "ensure:browser:firefox": "node ./scripts/ensure-playwright-browsers.mjs firefox",
65
65
  "ensure:browser:webkit": "node ./scripts/ensure-playwright-browsers.mjs webkit",
66
- "test:coverage": "npm run build && vitest run --coverage",
66
+ "test:coverage": "npm run build && node ../scripts/server-parity-native-addon.mjs --test-coverage",
67
67
  "lint": "eslint .",
68
68
  "verify:package": "node ./scripts/verify-package-exports.mjs",
69
69
  "prepack": "npm run build",
@@ -77,7 +77,7 @@
77
77
  "ws": "^8.21.2"
78
78
  },
79
79
  "optionalDependencies": {
80
- "@floegence/flowersec-node-native": "2.5.3"
80
+ "@floegence/flowersec-node-native": "3.0.0"
81
81
  },
82
82
  "devDependencies": {
83
83
  "@playwright/test": "1.62.1",
@@ -95,5 +95,5 @@
95
95
  "vite": "^8.2.1",
96
96
  "vitest": "4.1.10"
97
97
  },
98
- "flowersecSourceCommit": "aa50aab509e3519b2b9c8e49abccc407a831a741"
98
+ "flowersecSourceCommit": "ec1f9697daa70e4781cd460ec551d40df436335c"
99
99
  }
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.5",
4
- "serialNumber": "urn:uuid:ec0f2db1-aea5-5835-89bb-52b08c4a4a1c",
4
+ "serialNumber": "urn:uuid:c9faed3c-b0e2-55ed-8e12-98bc68091c8c",
5
5
  "version": 1,
6
6
  "metadata": {
7
7
  "component": {
8
8
  "type": "library",
9
9
  "name": "@floegence/flowersec-core",
10
- "version": "2.5.3",
11
- "purl": "pkg:npm/%40floegence/flowersec-core@2.5.3",
12
- "bom-ref": "pkg:npm/%40floegence/flowersec-core@2.5.3"
10
+ "version": "3.0.0",
11
+ "purl": "pkg:npm/%40floegence/flowersec-core@3.0.0",
12
+ "bom-ref": "pkg:npm/%40floegence/flowersec-core@3.0.0"
13
13
  },
14
14
  "properties": [
15
15
  {
16
16
  "name": "flowersec:source-inventory-sha256",
17
- "value": "1a8ba3adc252c3c72e7a5129a172b0c767a075161ac1d0379f0b257f091816fb"
17
+ "value": "8146654f4a197f2c5a7d100c8e25ee624d6f7ea2a6a57da0bc318ab1653b4a5c"
18
18
  }
19
19
  ]
20
20
  },
@@ -190,7 +190,7 @@
190
190
  ],
191
191
  "dependencies": [
192
192
  {
193
- "ref": "pkg:npm/%40floegence/flowersec-core@2.5.3",
193
+ "ref": "pkg:npm/%40floegence/flowersec-core@3.0.0",
194
194
  "dependsOn": [
195
195
  "pkg:npm/%40noble/ciphers@2.3.0",
196
196
  "pkg:npm/%40noble/curves@2.3.0",