@floegence/flowersec-core 5.0.4 → 5.0.5

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.
@@ -3,11 +3,11 @@
3
3
  This file is generated from the canonical Flowersec source dependency inventory for flowersec-ts.
4
4
  Do not edit it manually. License decisions are reviewed by the repository source license policy.
5
5
 
6
- - @floegence/flowersec-node-native 5.0.4 (Declared: MIT; selected: MIT; source: https://github.com/floegence/flowersec.git)
7
- - @floegence/flowersec-node-native-darwin-arm64 5.0.4 (Declared: MIT; selected: MIT; source: https://github.com/floegence/flowersec.git)
8
- - @floegence/flowersec-node-native-darwin-x64 5.0.4 (Declared: MIT; selected: MIT; source: https://github.com/floegence/flowersec.git)
9
- - @floegence/flowersec-node-native-linux-arm64-gnu 5.0.4 (Declared: MIT; selected: MIT; source: https://github.com/floegence/flowersec.git)
10
- - @floegence/flowersec-node-native-linux-x64-gnu 5.0.4 (Declared: MIT; selected: MIT; source: https://github.com/floegence/flowersec.git)
6
+ - @floegence/flowersec-node-native 5.0.5 (Declared: MIT; selected: MIT; source: https://github.com/floegence/flowersec.git)
7
+ - @floegence/flowersec-node-native-darwin-arm64 5.0.5 (Declared: MIT; selected: MIT; source: https://github.com/floegence/flowersec.git)
8
+ - @floegence/flowersec-node-native-darwin-x64 5.0.5 (Declared: MIT; selected: MIT; source: https://github.com/floegence/flowersec.git)
9
+ - @floegence/flowersec-node-native-linux-arm64-gnu 5.0.5 (Declared: MIT; selected: MIT; source: https://github.com/floegence/flowersec.git)
10
+ - @floegence/flowersec-node-native-linux-x64-gnu 5.0.5 (Declared: MIT; selected: MIT; source: https://github.com/floegence/flowersec.git)
11
11
  - @noble/ciphers 2.4.0 (Declared: MIT; selected: MIT; source: https://registry.npmjs.org/@noble/ciphers/-/ciphers-2.4.0.tgz)
12
12
  - @noble/curves 2.4.0 (Declared: MIT; selected: MIT; source: https://registry.npmjs.org/@noble/curves/-/curves-2.4.0.tgz)
13
13
  - @noble/hashes 2.4.0 (Declared: MIT; selected: MIT; source: https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz)
@@ -2,14 +2,16 @@ import { SDK_DEFAULTS } from "../defaults.js";
2
2
  import { claimArtifactLeaseV3, retireArtifactLeaseV3, } from "../v3/artifactLease.js";
3
3
  import { createConnectionControllerV3 as createCoreControllerV3, } from "../v3/connectionController.js";
4
4
  import { BrowserRuntimeCapabilityRegistryV3, createBrowserWebTransportCarrierV3, } from "../v3/browserRuntime.js";
5
- import { attemptClaimedArtifactLeaseV3, connectArtifactLeaseV3 } from "../v3/sessionConnector.js";
5
+ import { attemptClaimedArtifactLeaseV3, connectArtifactLeaseWithRuntimeV3, } from "../v3/sessionConnector.js";
6
6
  import { readyNativeAdmissionV3, readyWebSocketAdmissionV3 } from "../v3/runtimeAdapters.js";
7
7
  import { TransportFailureV3, ConnectErrorV3 } from "../v3/security.js";
8
8
  import { browserSessionRuntimeV3 } from "../v3/browserSessionRuntime.js";
9
9
  import { unwrapPrivateLoopbackArtifactLeaseV1, validatePrivateLoopbackOriginV1, } from "./privateLoopbackV1.js";
10
10
  export async function connectV3(lease, options = {}) {
11
- const registry = await BrowserRuntimeCapabilityRegistryV3.create();
12
- return await connectArtifactLeaseV3(lease, browserRuntime(registry, options.connectTimeoutMs), options.signal);
11
+ return await connectArtifactLeaseWithRuntimeV3(lease, options, async () => {
12
+ const registry = await BrowserRuntimeCapabilityRegistryV3.create();
13
+ return browserRuntime(registry, options.connectTimeoutMs);
14
+ });
13
15
  }
14
16
  export async function createConnectionControllerV3(source, options = {}) {
15
17
  const registry = await BrowserRuntimeCapabilityRegistryV3.create();
@@ -22,14 +24,15 @@ export async function createConnectionControllerV3(source, options = {}) {
22
24
  return createCoreControllerV3(source, async (context) => await attemptClaimedArtifactLeaseV3(context, runtime), coreOptions);
23
25
  }
24
26
  export async function connectPrivateLoopbackV1(lease, options) {
25
- const privateOrigin = requirePrivateLoopbackOrigin(options.origin);
26
27
  const unwrapped = unwrapPrivateLoopbackLease(lease);
27
- if (new URL(unwrapped.endpoint).origin.replace(/^ws:/, "http:") !== privateOrigin) {
28
- await retireInnerLease(unwrapped.innerLease);
29
- throw new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
30
- }
31
- const registry = await BrowserRuntimeCapabilityRegistryV3.create();
32
- return await connectArtifactLeaseV3(unwrapped.innerLease, privateLoopbackBrowserRuntime(registry, options.connectTimeoutMs, privateOrigin), options.signal);
28
+ return await connectArtifactLeaseWithRuntimeV3(unwrapped.innerLease, options, async () => {
29
+ const privateOrigin = requirePrivateLoopbackOrigin(options.origin);
30
+ if (new URL(unwrapped.endpoint).origin.replace(/^ws:/, "http:") !== privateOrigin) {
31
+ throw new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
32
+ }
33
+ const registry = await BrowserRuntimeCapabilityRegistryV3.create();
34
+ return privateLoopbackBrowserRuntime(registry, options.connectTimeoutMs, privateOrigin);
35
+ });
33
36
  }
34
37
  export async function createPrivateLoopbackConnectionControllerV1(source, options) {
35
38
  const privateOrigin = requirePrivateLoopbackOrigin(options.origin);
@@ -1,7 +1,7 @@
1
1
  import { SDK_DEFAULTS } from "../defaults.js";
2
2
  import { createConnectionControllerV3 as createCoreControllerV3, } from "../v3/connectionController.js";
3
3
  import { connectNodeWebSocketV3, detectNodeRuntimeCapabilityV3, } from "../v3/nodeRuntime.js";
4
- import { attemptClaimedArtifactLeaseV3, connectArtifactLeaseV3 } from "../v3/sessionConnector.js";
4
+ import { attemptClaimedArtifactLeaseV3, connectArtifactLeaseWithRuntimeV3, } from "../v3/sessionConnector.js";
5
5
  import { readyNativeAdmissionV3, readyWebSocketAdmissionV3 } from "../v3/runtimeAdapters.js";
6
6
  import { ConnectErrorV3, TransportFailureV3 } from "../v3/security.js";
7
7
  import { nodeSessionRuntimeV3 } from "../v3/nodeSessionRuntime.js";
@@ -9,7 +9,7 @@ import { createNativeRawQuicDriver, tryLoadNativeTransportAddon, } from "./nativ
9
9
  import { createNodeRawQuicClientV3 } from "./rawQuicAdapterV3.js";
10
10
  import { createRPCRouter, freezeRPCHandlers, } from "./acceptor.js";
11
11
  export async function connectV3(lease, options) {
12
- return await connectArtifactLeaseV3(lease, nodeRuntime(options), options.signal);
12
+ return await connectArtifactLeaseWithRuntimeV3(lease, options, () => nodeRuntime(options));
13
13
  }
14
14
  export function createConnectionControllerV3(source, options) {
15
15
  const runtime = nodeRuntime(options);
@@ -37,6 +37,7 @@ function nodeRuntime(options) {
37
37
  }
38
38
  return {
39
39
  capabilitySnapshot: () => capability,
40
+ candidateEligible: (candidate) => candidate.carrier !== "websocket" || options.origin !== undefined,
40
41
  connectTimeoutMilliseconds,
41
42
  protocolRuntime: nodeSessionRuntimeV3,
42
43
  ...(rpcSnapshot === undefined ? {} : { createRPCRouter: () => createRPCRouter(rpcSnapshot) }),
@@ -28,6 +28,7 @@ export class ProxyServerError extends Error {
28
28
  export class ProxyServer {
29
29
  #config;
30
30
  #active = new Set();
31
+ #abortLinks = new WeakMap();
31
32
  #permits = new Set();
32
33
  #completion;
33
34
  #resolveCompletion;
@@ -132,15 +133,20 @@ export class ProxyServer {
132
133
  return controller;
133
134
  }
134
135
  if (parent !== undefined) {
136
+ const abort = () => controller.abort(parent.reason);
135
137
  if (parent.aborted)
136
- controller.abort(parent.reason);
137
- else
138
- parent.addEventListener("abort", () => controller.abort(parent.reason), { once: true });
138
+ abort();
139
+ else {
140
+ parent.addEventListener("abort", abort, { once: true });
141
+ this.#abortLinks.set(controller, () => parent.removeEventListener("abort", abort));
142
+ }
139
143
  }
140
144
  this.#active.add(controller);
141
145
  return controller;
142
146
  }
143
147
  #untrack(controller) {
148
+ this.#abortLinks.get(controller)?.();
149
+ this.#abortLinks.delete(controller);
144
150
  this.#active.delete(controller);
145
151
  if (this.#closed && this.#active.size === 0)
146
152
  this.#resolveCompletion();
@@ -106,11 +106,14 @@ export class RpcClient {
106
106
  while (!this.closed) {
107
107
  const v = assertRpcEnvelope(await readJsonFrame(this.readExactly, DEFAULT_MAX_JSON_FRAME_BYTES));
108
108
  if (v.response_to === 0) {
109
- // Notification: response_to=0 and request_id=0.
110
- if (v.request_id === 0) {
111
- const set = this.notifyHandlers.get(v.type_id);
112
- if (set != null) {
113
- for (const h of set) {
109
+ if (v.request_id !== 0) {
110
+ throw new Error("rpc invalid request on client stream");
111
+ }
112
+ const set = this.notifyHandlers.get(v.type_id);
113
+ if (set != null) {
114
+ const handlers = [...set];
115
+ setTimeout(() => {
116
+ for (const h of handlers) {
114
117
  try {
115
118
  h(v.payload);
116
119
  }
@@ -118,7 +121,7 @@ export class RpcClient {
118
121
  // User handlers should not be able to take down the transport read loop.
119
122
  }
120
123
  }
121
- }
124
+ });
122
125
  }
123
126
  continue;
124
127
  }
@@ -623,7 +623,7 @@ function normalizeAuthority(authority) {
623
623
  portText = tail.slice(1);
624
624
  }
625
625
  if (host.includes("."))
626
- throw invalidCandidate("IPv6 dotted subset");
626
+ throw invalidCandidate("embedded IPv4");
627
627
  let parsed;
628
628
  try {
629
629
  parsed = new URL(`http://[${host}]/`);
@@ -33,9 +33,35 @@ export async function connectNodeTLSSocketV3(candidate, attemptNowUnixSeconds, o
33
33
  ALPNProtocols: ["http/1.1"],
34
34
  secureOptions: constants.SSL_OP_NO_TICKET,
35
35
  rejectUnauthorized: policy.mode === "ca",
36
- ...(isIPAddress(host)
37
- ? { checkServerIdentity: (_servername, certificate) => tlsCheckServerIdentity(host, certificate) }
38
- : { servername: host }),
36
+ ...(policy.mode === "ca"
37
+ ? (isIPAddress(host)
38
+ ? { checkServerIdentity: (_servername, certificate) => {
39
+ const subjectAltName = certificate.subjectaltname;
40
+ if (typeof subjectAltName !== "string" || !/(?:^|,\s*)(?:DNS|IP Address):/.test(subjectAltName)) {
41
+ return Object.assign(new Error("certificate is missing a DNS-ID or IP-ID subjectAltName"), {
42
+ code: "ERR_TLS_CERT_ALTNAME_INVALID",
43
+ });
44
+ }
45
+ return tlsCheckServerIdentity(host, certificate);
46
+ } }
47
+ : {
48
+ servername: host,
49
+ checkServerIdentity: (_servername, certificate) => {
50
+ // Node's historical hostname verifier may fall back to CN when the
51
+ // certificate has no subjectAltName. Transport v3 requires a DNS-ID
52
+ // or IP-ID SAN; CN alone is never an identity proof.
53
+ const subjectAltName = certificate.subjectaltname;
54
+ if (typeof subjectAltName !== "string" || !/(?:^|,\s*)(?:DNS|IP Address):/.test(subjectAltName)) {
55
+ return Object.assign(new Error("certificate is missing a DNS-ID or IP-ID subjectAltName"), {
56
+ code: "ERR_TLS_CERT_ALTNAME_INVALID",
57
+ });
58
+ }
59
+ return tlsCheckServerIdentity(host, certificate);
60
+ },
61
+ })
62
+ : isIPAddress(host)
63
+ ? { checkServerIdentity: (_servername, certificate) => tlsCheckServerIdentity(host, certificate) }
64
+ : { servername: host }),
39
65
  ...(policy.mode === "ca" && options.roots !== undefined ? { ca: options.roots } : {}),
40
66
  };
41
67
  const timeout = options.timeoutMilliseconds ?? 10_000;
@@ -64,7 +90,7 @@ export async function connectNodeTLSSocketV3(candidate, attemptNowUnixSeconds, o
64
90
  if (policy.mode === "ca" && isNodeCertificateTrustErrorV3(error.code)) {
65
91
  fail(new TransportFailureV3("tls_failed", "ca_untrusted", error));
66
92
  }
67
- else if (policy.mode === "pin" && isTLSProtocolError(error.code)) {
93
+ else if (isTLSProtocolError(error.code)) {
68
94
  fail(new TransportFailureV3("tls_failed", "unknown", error));
69
95
  }
70
96
  else {
@@ -89,6 +115,12 @@ export async function connectNodeTLSSocketV3(candidate, attemptNowUnixSeconds, o
89
115
  }
90
116
  settled = true;
91
117
  cleanup();
118
+ // Keep a settled error sink attached until close. A live TLSSocket may
119
+ // emit asynchronous errors after the handshake; letting those escape
120
+ // without a listener turns a recoverable socket failure into an
121
+ // uncaught Node process error.
122
+ socket.on("error", onError);
123
+ socket.once("close", () => socket.removeListener("error", onError));
92
124
  resolve(socket);
93
125
  });
94
126
  if (options.signal?.aborted === true)
@@ -460,7 +460,7 @@ export class SessionV3 {
460
460
  while (this.lifecycle === "open") {
461
461
  const carrierStream = await this.carrier.acceptStream();
462
462
  void this.acceptCarrierStream(carrierStream).catch((error) => {
463
- void carrierStream.reset();
463
+ void carrierStream.reset().catch(() => undefined);
464
464
  if (error instanceof StreamLifetimeLedgerV3Error && error.code === "duplicate") {
465
465
  this.fail(protocolError(error.message));
466
466
  }
@@ -26,6 +26,7 @@ export type ReadyAdmissionTransportV3 = Readonly<{
26
26
  export type CandidateDialerV3 = (candidate: CanonicalArtifactCandidateV3, artifact: ArtifactV3, attemptNowUnixSeconds: number, capability: RuntimeCapabilityDescriptorV3, signal: AbortSignal) => Promise<ReadyAdmissionTransportV3>;
27
27
  export type SessionConnectorRuntimeV3 = Readonly<{
28
28
  capabilitySnapshot(): RuntimeCapabilityDescriptorV3;
29
+ candidateEligible?: (candidate: CanonicalArtifactCandidateV3) => boolean;
29
30
  dial: CandidateDialerV3;
30
31
  protocolRuntime: SessionProtocolRuntimeV3;
31
32
  connectTimeoutMilliseconds?: number;
@@ -34,4 +35,8 @@ export type SessionConnectorRuntimeV3 = Readonly<{
34
35
  deadlineFactory?: SessionDeadlineFactoryV3;
35
36
  }>;
36
37
  export declare function connectArtifactLeaseV3(lease: ArtifactLeaseV3, runtime: SessionConnectorRuntimeV3, signal?: AbortSignal): Promise<Session>;
38
+ export declare function connectArtifactLeaseWithRuntimeV3(lease: ArtifactLeaseV3, options: Readonly<{
39
+ connectTimeoutMs?: number;
40
+ signal?: AbortSignal;
41
+ }>, createRuntime: (signal: AbortSignal) => SessionConnectorRuntimeV3 | Promise<SessionConnectorRuntimeV3>): Promise<Session>;
37
42
  export declare function attemptClaimedArtifactLeaseV3(context: LeaseAttemptContextV3, runtime: SessionConnectorRuntimeV3): Promise<LeaseAttemptResultV3<Session>>;
@@ -15,7 +15,58 @@ export async function connectArtifactLeaseV3(lease, runtime, signal) {
15
15
  catch {
16
16
  throw new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
17
17
  }
18
- const artifact = claimedArtifactV3(claim);
18
+ return await connectClaimedArtifactLeaseV3(claim, runtime, signal);
19
+ }
20
+ export async function connectArtifactLeaseWithRuntimeV3(lease, options, createRuntime) {
21
+ let claim;
22
+ try {
23
+ claim = claimArtifactLeaseV3(lease);
24
+ }
25
+ catch {
26
+ throw new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
27
+ }
28
+ let timeout;
29
+ let signal;
30
+ try {
31
+ timeout = normalizeConnectTimeoutMilliseconds(options.connectTimeoutMs);
32
+ signal = options.signal;
33
+ }
34
+ catch (error) {
35
+ await retireArtifactLeaseV3(claim);
36
+ throw publicPreSpendError(error);
37
+ }
38
+ const controller = new AbortController();
39
+ const unlink = linkPublicConnectAbort(signal, controller);
40
+ const timer = setTimeout(() => controller.abort(new ConnectErrorV3("connection_failed", { kind: "retryable" })), timeout);
41
+ try {
42
+ throwIfAborted(controller.signal);
43
+ let runtime;
44
+ try {
45
+ runtime = await raceAbort(Promise.resolve().then(() => createRuntime(controller.signal)), controller.signal);
46
+ }
47
+ catch (error) {
48
+ throw publicPreSpendError(error);
49
+ }
50
+ return await connectClaimedArtifactLeaseV3(claim, { ...runtime, connectTimeoutMilliseconds: timeout }, controller.signal, true);
51
+ }
52
+ catch (error) {
53
+ if (artifactLeaseStateV3(claim) === "claimed")
54
+ await retireArtifactLeaseV3(claim);
55
+ throw error;
56
+ }
57
+ finally {
58
+ clearTimeout(timer);
59
+ unlink();
60
+ }
61
+ }
62
+ async function connectClaimedArtifactLeaseV3(claim, runtime, signal, deadlineStarted = false) {
63
+ let artifact;
64
+ try {
65
+ artifact = claimedArtifactV3(claim);
66
+ }
67
+ catch {
68
+ throw new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
69
+ }
19
70
  const now = runtime.nowUnixSeconds ?? (() => Math.floor(Date.now() / 1_000));
20
71
  let capability;
21
72
  let candidates;
@@ -33,7 +84,7 @@ export async function connectArtifactLeaseV3(lease, runtime, signal) {
33
84
  const controller = new AbortController();
34
85
  const unlink = linkAbort(signal, controller);
35
86
  try {
36
- const result = await attemptClaimedArtifactLeaseV3({
87
+ const context = {
37
88
  kind: "primary",
38
89
  artifact,
39
90
  candidates,
@@ -41,7 +92,10 @@ export async function connectArtifactLeaseV3(lease, runtime, signal) {
41
92
  signal: controller.signal,
42
93
  capability,
43
94
  assertArtifactFresh: () => assertArtifactFresh(artifact, now),
44
- }, runtime);
95
+ };
96
+ const result = deadlineStarted
97
+ ? await attemptClaimedArtifactLeaseUnderSignalV3(context, runtime)
98
+ : await attemptClaimedArtifactLeaseV3(context, runtime);
45
99
  if (result.kind === "established")
46
100
  return result.session;
47
101
  if (artifactLeaseStateV3(claim) === "claimed")
@@ -70,8 +124,17 @@ export async function attemptClaimedArtifactLeaseV3(context, runtime) {
70
124
  const unlink = linkConnectAttemptAbort(context.signal, controller);
71
125
  const timer = setTimeout(() => controller.abort(new ConnectErrorV3("connection_failed", { kind: "retryable" })), timeoutMilliseconds);
72
126
  try {
73
- throwIfAborted(controller.signal);
74
- return await attemptClaimedArtifactLeaseWithinDeadlineV3({ ...context, signal: controller.signal }, runtime);
127
+ return await attemptClaimedArtifactLeaseUnderSignalV3({ ...context, signal: controller.signal }, runtime);
128
+ }
129
+ finally {
130
+ clearTimeout(timer);
131
+ unlink();
132
+ }
133
+ }
134
+ async function attemptClaimedArtifactLeaseUnderSignalV3(context, runtime) {
135
+ try {
136
+ throwIfAborted(context.signal);
137
+ return await attemptClaimedArtifactLeaseWithinDeadlineV3(context, runtime);
75
138
  }
76
139
  catch (error) {
77
140
  const projected = error instanceof ConnectErrorV3
@@ -81,10 +144,6 @@ export async function attemptClaimedArtifactLeaseV3(context, runtime) {
81
144
  ? { kind: "pre_spend_failure", error: projected }
82
145
  : { kind: "post_spend_failure", error: projected };
83
146
  }
84
- finally {
85
- clearTimeout(timer);
86
- unlink();
87
- }
88
147
  }
89
148
  async function attemptClaimedArtifactLeaseWithinDeadlineV3(context, runtime) {
90
149
  const role = context.artifact.path.kind === "tunnel" && context.artifact.path.role === 2
@@ -115,6 +174,10 @@ async function attemptClaimedArtifactLeaseWithinDeadlineV3(context, runtime) {
115
174
  }
116
175
  for (const [index, candidate] of context.candidates.entries()) {
117
176
  throwIfAborted(context.signal);
177
+ if (runtime.candidateEligible?.(candidate) === false) {
178
+ failures.push({ candidate, failure: new TransportFailureV3("tls_unsupported") });
179
+ continue;
180
+ }
118
181
  if (!supportsCandidateSecurityV3(context.capability, candidate.carrier, context.artifact.path.kind, role, candidate.tls.mode)) {
119
182
  failures.push({ candidate, failure: new TransportFailureV3("tls_unsupported") });
120
183
  continue;
@@ -378,6 +441,16 @@ function linkConnectAttemptAbort(parent, child) {
378
441
  parent.addEventListener("abort", abort, { once: true });
379
442
  return () => parent.removeEventListener("abort", abort);
380
443
  }
444
+ function linkPublicConnectAbort(parent, child) {
445
+ if (parent === undefined)
446
+ return () => undefined;
447
+ const abort = () => child.abort(new ConnectErrorV3("connection_failed", { kind: "terminal" }));
448
+ if (parent.aborted)
449
+ abort();
450
+ else
451
+ parent.addEventListener("abort", abort, { once: true });
452
+ return () => parent.removeEventListener("abort", abort);
453
+ }
381
454
  function throwIfAborted(signal) {
382
455
  if (signal.aborted)
383
456
  throw signal.reason;
@@ -247,7 +247,8 @@ function throwIfAborted(signal) {
247
247
  }
248
248
  function isAbortError(error) {
249
249
  return error instanceof DOMException && error.name === "AbortError" ||
250
- error instanceof Error && error.name === "AbortError";
250
+ error instanceof Error && (error.name === "AbortError" ||
251
+ error.code === "aborted");
251
252
  }
252
253
  function labelWith(label, ...parts) {
253
254
  return concat(encoder.encode(label), byte(0), ...parts);
@@ -188,22 +188,40 @@ class WebTransportCarrierStreamAdapter {
188
188
  stopSendingPromise;
189
189
  resetPromise;
190
190
  released = false;
191
+ pendingRead = false;
191
192
  constructor(stream, onRelease) {
192
193
  this.onRelease = onRelease;
193
194
  this.reader = stream.readable.getReader();
194
195
  this.writer = stream.writable.getWriter();
195
196
  }
196
- async read(options = {}) {
197
+ async read(_options = {}) {
197
198
  this.assertReadable();
198
199
  if (this.readClosed)
199
200
  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;
201
+ this.pendingRead = true;
202
+ try {
203
+ const result = await carrierCall(this.reader.read(), "WebTransport stream read failed", "reset");
204
+ if (result.done || result.value === undefined) {
205
+ this.readClosed = true;
206
+ this.releaseIfComplete();
207
+ return null;
208
+ }
209
+ return result.value.slice();
210
+ }
211
+ finally {
212
+ this.pendingRead = false;
205
213
  }
206
- return result.value.slice();
214
+ }
215
+ cancelPending() {
216
+ if (this.terminalError !== undefined || !this.pendingRead) {
217
+ if (!this.pendingRead && this.terminalError === undefined)
218
+ this.abort(abortedCarrierError());
219
+ return;
220
+ }
221
+ this.readClosed = true;
222
+ void this.reader.cancel(abortedCarrierError()).catch(() => undefined).finally(() => {
223
+ this.releaseIfComplete();
224
+ });
207
225
  }
208
226
  async write(data) {
209
227
  this.assertNotTerminal();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floegence/flowersec-core",
3
- "version": "5.0.4",
3
+ "version": "5.0.5",
4
4
  "description": "Flowersec core TypeScript library for carrier-neutral encrypted sessions and multiplexed streams.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -95,7 +95,7 @@
95
95
  "vitest": "4.1.11"
96
96
  },
97
97
  "optionalDependencies": {
98
- "@floegence/flowersec-node-native": "5.0.4"
98
+ "@floegence/flowersec-node-native": "5.0.5"
99
99
  },
100
- "flowersecSourceCommit": "c7f7ef0ab82ca329703bd727433075f2996f1127"
100
+ "flowersecSourceCommit": "9ce4dcaa09c33b7c07261575768ee78b3170bbec"
101
101
  }
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.5",
4
- "serialNumber": "urn:uuid:6039ab01-9a2a-5a20-87ae-899bb738f3d0",
4
+ "serialNumber": "urn:uuid:e3cc825d-1afc-5661-8afa-202f2c82d8b5",
5
5
  "version": 1,
6
6
  "metadata": {
7
7
  "component": {
8
8
  "type": "library",
9
9
  "name": "@floegence/flowersec-core",
10
- "version": "5.0.4",
11
- "purl": "pkg:npm/%40floegence/flowersec-core@5.0.4",
12
- "bom-ref": "pkg:npm/%40floegence/flowersec-core@5.0.4"
10
+ "version": "5.0.5",
11
+ "purl": "pkg:npm/%40floegence/flowersec-core@5.0.5",
12
+ "bom-ref": "pkg:npm/%40floegence/flowersec-core@5.0.5"
13
13
  },
14
14
  "properties": [
15
15
  {
16
16
  "name": "flowersec:source-inventory-sha256",
17
- "value": "1d811c3e752603c670a7bd1edb9032d5ebc3bbb8e7dad0c537b40e0fd7c157d8"
17
+ "value": "f52ee88ce9361e3a5f87dfa29bbc3f70c025c20fb2a0b274cf90e88a5f773f50"
18
18
  }
19
19
  ]
20
20
  },
@@ -22,9 +22,9 @@
22
22
  {
23
23
  "type": "library",
24
24
  "name": "@floegence/flowersec-node-native",
25
- "version": "5.0.4",
26
- "purl": "pkg:npm/%40floegence/flowersec-node-native@5.0.4",
27
- "bom-ref": "pkg:npm/%40floegence/flowersec-node-native@5.0.4",
25
+ "version": "5.0.5",
26
+ "purl": "pkg:npm/%40floegence/flowersec-node-native@5.0.5",
27
+ "bom-ref": "pkg:npm/%40floegence/flowersec-node-native@5.0.5",
28
28
  "licenses": [
29
29
  {
30
30
  "expression": "MIT"
@@ -50,9 +50,9 @@
50
50
  {
51
51
  "type": "library",
52
52
  "name": "@floegence/flowersec-node-native-darwin-arm64",
53
- "version": "5.0.4",
54
- "purl": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@5.0.4",
55
- "bom-ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@5.0.4",
53
+ "version": "5.0.5",
54
+ "purl": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@5.0.5",
55
+ "bom-ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@5.0.5",
56
56
  "licenses": [
57
57
  {
58
58
  "expression": "MIT"
@@ -78,9 +78,9 @@
78
78
  {
79
79
  "type": "library",
80
80
  "name": "@floegence/flowersec-node-native-darwin-x64",
81
- "version": "5.0.4",
82
- "purl": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@5.0.4",
83
- "bom-ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@5.0.4",
81
+ "version": "5.0.5",
82
+ "purl": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@5.0.5",
83
+ "bom-ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@5.0.5",
84
84
  "licenses": [
85
85
  {
86
86
  "expression": "MIT"
@@ -106,9 +106,9 @@
106
106
  {
107
107
  "type": "library",
108
108
  "name": "@floegence/flowersec-node-native-linux-arm64-gnu",
109
- "version": "5.0.4",
110
- "purl": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@5.0.4",
111
- "bom-ref": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@5.0.4",
109
+ "version": "5.0.5",
110
+ "purl": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@5.0.5",
111
+ "bom-ref": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@5.0.5",
112
112
  "licenses": [
113
113
  {
114
114
  "expression": "MIT"
@@ -134,9 +134,9 @@
134
134
  {
135
135
  "type": "library",
136
136
  "name": "@floegence/flowersec-node-native-linux-x64-gnu",
137
- "version": "5.0.4",
138
- "purl": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@5.0.4",
139
- "bom-ref": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@5.0.4",
137
+ "version": "5.0.5",
138
+ "purl": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@5.0.5",
139
+ "bom-ref": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@5.0.5",
140
140
  "licenses": [
141
141
  {
142
142
  "expression": "MIT"
@@ -330,9 +330,9 @@
330
330
  ],
331
331
  "dependencies": [
332
332
  {
333
- "ref": "pkg:npm/%40floegence/flowersec-core@5.0.4",
333
+ "ref": "pkg:npm/%40floegence/flowersec-core@5.0.5",
334
334
  "dependsOn": [
335
- "pkg:npm/%40floegence/flowersec-node-native@5.0.4",
335
+ "pkg:npm/%40floegence/flowersec-node-native@5.0.5",
336
336
  "pkg:npm/%40noble/ciphers@2.4.0",
337
337
  "pkg:npm/%40noble/curves@2.4.0",
338
338
  "pkg:npm/%40noble/hashes@2.4.0",
@@ -341,28 +341,28 @@
341
341
  ]
342
342
  },
343
343
  {
344
- "ref": "pkg:npm/%40floegence/flowersec-node-native@5.0.4",
344
+ "ref": "pkg:npm/%40floegence/flowersec-node-native@5.0.5",
345
345
  "dependsOn": [
346
- "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@5.0.4",
347
- "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@5.0.4",
348
- "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@5.0.4",
349
- "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@5.0.4"
346
+ "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@5.0.5",
347
+ "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@5.0.5",
348
+ "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@5.0.5",
349
+ "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@5.0.5"
350
350
  ]
351
351
  },
352
352
  {
353
- "ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@5.0.4",
353
+ "ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@5.0.5",
354
354
  "dependsOn": []
355
355
  },
356
356
  {
357
- "ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@5.0.4",
357
+ "ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@5.0.5",
358
358
  "dependsOn": []
359
359
  },
360
360
  {
361
- "ref": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@5.0.4",
361
+ "ref": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@5.0.5",
362
362
  "dependsOn": []
363
363
  },
364
364
  {
365
- "ref": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@5.0.4",
365
+ "ref": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@5.0.5",
366
366
  "dependsOn": []
367
367
  },
368
368
  {
package/sbom/spdx.json CHANGED
@@ -3,9 +3,9 @@
3
3
  "dataLicense": "CC0-1.0",
4
4
  "SPDXID": "SPDXRef-DOCUMENT",
5
5
  "name": "flowersec-ts",
6
- "documentNamespace": "https://github.com/floegence/flowersec/sbom/flowersec-ts/1d811c3e752603c670a7bd1edb9032d5ebc3bbb8e7dad0c537b40e0fd7c157d8",
6
+ "documentNamespace": "https://github.com/floegence/flowersec/sbom/flowersec-ts/f52ee88ce9361e3a5f87dfa29bbc3f70c025c20fb2a0b274cf90e88a5f773f50",
7
7
  "creationInfo": {
8
- "created": "2026-09-04T01:34:01Z",
8
+ "created": "2026-09-07T04:29:43Z",
9
9
  "creators": [
10
10
  "Tool: flowersec-source-inventory-1"
11
11
  ]
@@ -13,26 +13,26 @@
13
13
  "packages": [
14
14
  {
15
15
  "name": "@floegence/flowersec-core",
16
- "SPDXID": "SPDXRef-Package-76f76655df85108ee0d0",
17
- "versionInfo": "5.0.4",
16
+ "SPDXID": "SPDXRef-Package-453dd3dbe8557f33f1ac",
17
+ "versionInfo": "5.0.5",
18
18
  "downloadLocation": "NOASSERTION",
19
19
  "filesAnalyzed": false,
20
20
  "licenseConcluded": "NOASSERTION",
21
21
  "licenseDeclared": "NOASSERTION",
22
22
  "copyrightText": "NOASSERTION",
23
- "comment": "Flowersec source inventory SHA-256: 1d811c3e752603c670a7bd1edb9032d5ebc3bbb8e7dad0c537b40e0fd7c157d8",
23
+ "comment": "Flowersec source inventory SHA-256: f52ee88ce9361e3a5f87dfa29bbc3f70c025c20fb2a0b274cf90e88a5f773f50",
24
24
  "externalRefs": [
25
25
  {
26
26
  "referenceCategory": "PACKAGE-MANAGER",
27
27
  "referenceType": "purl",
28
- "referenceLocator": "pkg:npm/%40floegence/flowersec-core@5.0.4"
28
+ "referenceLocator": "pkg:npm/%40floegence/flowersec-core@5.0.5"
29
29
  }
30
30
  ]
31
31
  },
32
32
  {
33
33
  "name": "@floegence/flowersec-node-native",
34
- "SPDXID": "SPDXRef-Package-eaba5905b7c607ccf435",
35
- "versionInfo": "5.0.4",
34
+ "SPDXID": "SPDXRef-Package-490a355ce8395380d6e7",
35
+ "versionInfo": "5.0.5",
36
36
  "downloadLocation": "https://github.com/floegence/flowersec.git",
37
37
  "filesAnalyzed": false,
38
38
  "licenseConcluded": "MIT",
@@ -42,14 +42,14 @@
42
42
  {
43
43
  "referenceCategory": "PACKAGE-MANAGER",
44
44
  "referenceType": "purl",
45
- "referenceLocator": "pkg:npm/%40floegence/flowersec-node-native@5.0.4"
45
+ "referenceLocator": "pkg:npm/%40floegence/flowersec-node-native@5.0.5"
46
46
  }
47
47
  ]
48
48
  },
49
49
  {
50
50
  "name": "@floegence/flowersec-node-native-darwin-arm64",
51
- "SPDXID": "SPDXRef-Package-7c37536aff97144c437d",
52
- "versionInfo": "5.0.4",
51
+ "SPDXID": "SPDXRef-Package-2a8533025dcddb70e50f",
52
+ "versionInfo": "5.0.5",
53
53
  "downloadLocation": "https://github.com/floegence/flowersec.git",
54
54
  "filesAnalyzed": false,
55
55
  "licenseConcluded": "MIT",
@@ -59,14 +59,14 @@
59
59
  {
60
60
  "referenceCategory": "PACKAGE-MANAGER",
61
61
  "referenceType": "purl",
62
- "referenceLocator": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@5.0.4"
62
+ "referenceLocator": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@5.0.5"
63
63
  }
64
64
  ]
65
65
  },
66
66
  {
67
67
  "name": "@floegence/flowersec-node-native-darwin-x64",
68
- "SPDXID": "SPDXRef-Package-810d8647e49cab312ddf",
69
- "versionInfo": "5.0.4",
68
+ "SPDXID": "SPDXRef-Package-7708f9d84a482ac3cb09",
69
+ "versionInfo": "5.0.5",
70
70
  "downloadLocation": "https://github.com/floegence/flowersec.git",
71
71
  "filesAnalyzed": false,
72
72
  "licenseConcluded": "MIT",
@@ -76,14 +76,14 @@
76
76
  {
77
77
  "referenceCategory": "PACKAGE-MANAGER",
78
78
  "referenceType": "purl",
79
- "referenceLocator": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@5.0.4"
79
+ "referenceLocator": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@5.0.5"
80
80
  }
81
81
  ]
82
82
  },
83
83
  {
84
84
  "name": "@floegence/flowersec-node-native-linux-arm64-gnu",
85
- "SPDXID": "SPDXRef-Package-16ea1010a290d52cbeae",
86
- "versionInfo": "5.0.4",
85
+ "SPDXID": "SPDXRef-Package-0cab5a1460c5399db58c",
86
+ "versionInfo": "5.0.5",
87
87
  "downloadLocation": "https://github.com/floegence/flowersec.git",
88
88
  "filesAnalyzed": false,
89
89
  "licenseConcluded": "MIT",
@@ -93,14 +93,14 @@
93
93
  {
94
94
  "referenceCategory": "PACKAGE-MANAGER",
95
95
  "referenceType": "purl",
96
- "referenceLocator": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@5.0.4"
96
+ "referenceLocator": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@5.0.5"
97
97
  }
98
98
  ]
99
99
  },
100
100
  {
101
101
  "name": "@floegence/flowersec-node-native-linux-x64-gnu",
102
- "SPDXID": "SPDXRef-Package-9f150aef88bc8208a2ed",
103
- "versionInfo": "5.0.4",
102
+ "SPDXID": "SPDXRef-Package-924889f967e0c3d57857",
103
+ "versionInfo": "5.0.5",
104
104
  "downloadLocation": "https://github.com/floegence/flowersec.git",
105
105
  "filesAnalyzed": false,
106
106
  "licenseConcluded": "MIT",
@@ -110,7 +110,7 @@
110
110
  {
111
111
  "referenceCategory": "PACKAGE-MANAGER",
112
112
  "referenceType": "purl",
113
- "referenceLocator": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@5.0.4"
113
+ "referenceLocator": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@5.0.5"
114
114
  }
115
115
  ]
116
116
  },
@@ -221,57 +221,57 @@
221
221
  {
222
222
  "spdxElementId": "SPDXRef-DOCUMENT",
223
223
  "relationshipType": "DESCRIBES",
224
- "relatedSpdxElement": "SPDXRef-Package-76f76655df85108ee0d0"
224
+ "relatedSpdxElement": "SPDXRef-Package-453dd3dbe8557f33f1ac"
225
225
  },
226
226
  {
227
- "spdxElementId": "SPDXRef-Package-76f76655df85108ee0d0",
227
+ "spdxElementId": "SPDXRef-Package-453dd3dbe8557f33f1ac",
228
228
  "relationshipType": "DEPENDS_ON",
229
- "relatedSpdxElement": "SPDXRef-Package-eaba5905b7c607ccf435"
229
+ "relatedSpdxElement": "SPDXRef-Package-490a355ce8395380d6e7"
230
230
  },
231
231
  {
232
- "spdxElementId": "SPDXRef-Package-76f76655df85108ee0d0",
232
+ "spdxElementId": "SPDXRef-Package-453dd3dbe8557f33f1ac",
233
233
  "relationshipType": "DEPENDS_ON",
234
234
  "relatedSpdxElement": "SPDXRef-Package-6cdb8b3664bfb0d67b6a"
235
235
  },
236
236
  {
237
- "spdxElementId": "SPDXRef-Package-76f76655df85108ee0d0",
237
+ "spdxElementId": "SPDXRef-Package-453dd3dbe8557f33f1ac",
238
238
  "relationshipType": "DEPENDS_ON",
239
239
  "relatedSpdxElement": "SPDXRef-Package-e0e2cb449dffb239a794"
240
240
  },
241
241
  {
242
- "spdxElementId": "SPDXRef-Package-76f76655df85108ee0d0",
242
+ "spdxElementId": "SPDXRef-Package-453dd3dbe8557f33f1ac",
243
243
  "relationshipType": "DEPENDS_ON",
244
244
  "relatedSpdxElement": "SPDXRef-Package-2e6e3840217975457b7a"
245
245
  },
246
246
  {
247
- "spdxElementId": "SPDXRef-Package-76f76655df85108ee0d0",
247
+ "spdxElementId": "SPDXRef-Package-453dd3dbe8557f33f1ac",
248
248
  "relationshipType": "DEPENDS_ON",
249
249
  "relatedSpdxElement": "SPDXRef-Package-5b9d78698ec0a5c8f86f"
250
250
  },
251
251
  {
252
- "spdxElementId": "SPDXRef-Package-76f76655df85108ee0d0",
252
+ "spdxElementId": "SPDXRef-Package-453dd3dbe8557f33f1ac",
253
253
  "relationshipType": "DEPENDS_ON",
254
254
  "relatedSpdxElement": "SPDXRef-Package-b779412f685822663496"
255
255
  },
256
256
  {
257
- "spdxElementId": "SPDXRef-Package-eaba5905b7c607ccf435",
257
+ "spdxElementId": "SPDXRef-Package-490a355ce8395380d6e7",
258
258
  "relationshipType": "DEPENDS_ON",
259
- "relatedSpdxElement": "SPDXRef-Package-7c37536aff97144c437d"
259
+ "relatedSpdxElement": "SPDXRef-Package-2a8533025dcddb70e50f"
260
260
  },
261
261
  {
262
- "spdxElementId": "SPDXRef-Package-eaba5905b7c607ccf435",
262
+ "spdxElementId": "SPDXRef-Package-490a355ce8395380d6e7",
263
263
  "relationshipType": "DEPENDS_ON",
264
- "relatedSpdxElement": "SPDXRef-Package-810d8647e49cab312ddf"
264
+ "relatedSpdxElement": "SPDXRef-Package-7708f9d84a482ac3cb09"
265
265
  },
266
266
  {
267
- "spdxElementId": "SPDXRef-Package-eaba5905b7c607ccf435",
267
+ "spdxElementId": "SPDXRef-Package-490a355ce8395380d6e7",
268
268
  "relationshipType": "DEPENDS_ON",
269
- "relatedSpdxElement": "SPDXRef-Package-16ea1010a290d52cbeae"
269
+ "relatedSpdxElement": "SPDXRef-Package-0cab5a1460c5399db58c"
270
270
  },
271
271
  {
272
- "spdxElementId": "SPDXRef-Package-eaba5905b7c607ccf435",
272
+ "spdxElementId": "SPDXRef-Package-490a355ce8395380d6e7",
273
273
  "relationshipType": "DEPENDS_ON",
274
- "relatedSpdxElement": "SPDXRef-Package-9f150aef88bc8208a2ed"
274
+ "relatedSpdxElement": "SPDXRef-Package-924889f967e0c3d57857"
275
275
  },
276
276
  {
277
277
  "spdxElementId": "SPDXRef-Package-e0e2cb449dffb239a794",