@cortexkit/subc-client
Advanced tools
+1
-1
| { | ||
| "name": "@cortexkit/subc-client", | ||
| "version": "0.2.0", | ||
| "version": "0.2.1", | ||
| "description": "TypeScript client for the subc daemon. Wire-compatible (byte-for-byte) with the Rust subc-transport handshake and subc-protocol envelope.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+20
-3
@@ -524,3 +524,3 @@ // The consumer-facing subc client. Mirrors the canonical pure consumer | ||
| pending.timer = setTimeout(() => { | ||
| this.rejectPending(key, pending, new SubcError(`request on channel ${channel} timed out after ${ms}ms`)); | ||
| this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms))); | ||
| }, ms); | ||
@@ -590,3 +590,3 @@ this.pending.set(key, pending); | ||
| pending.timer = setTimeout(() => { | ||
| this.rejectPending(key, pending, new SubcError(`request on channel ${channel} timed out after ${ms}ms`)); | ||
| this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms))); | ||
| }, ms); | ||
@@ -757,3 +757,10 @@ this.pending.set(key, pending); | ||
| if (cached.closed) continue; // closed concurrently with reconnect — don't reopen. | ||
| const channel = await this.routeOpen(cached.target, cached.identity); | ||
| // Thread the route's consumer identity through the reopen, exactly as the | ||
| // lazy per-call path (openCachedRoute) does. Dropping it here would make a | ||
| // route reopened after a reconnect send route.open with no consumer_identity, | ||
| // so the daemon would re-stamp it with a different (weaker) principal than the | ||
| // one it was originally bound under — a silent post-reconnect trust downgrade. | ||
| const channel = await this.routeOpen(cached.target, cached.identity, { | ||
| consumerIdentity: cached.consumerIdentity ?? null, | ||
| }); | ||
| // A closeRoute may have raced this reopen (flipping the tombstone during the | ||
@@ -771,2 +778,12 @@ // route.open await). If so, GOODBYE the channel instead of installing it, so the | ||
| // A request timeout carries the local socket port and (channel, corr) so a | ||
| // packet capture can pinpoint the exact on-wire exchange — the decisive evidence | ||
| // for whether a "timed out" reply was actually delivered to this socket (a | ||
| // client-local demux problem) or never sent (a daemon/module problem). | ||
| private timeoutMessage(channel: number, corr: bigint, ms: number): string { | ||
| const port = this.sock.localPort(); | ||
| const where = port === null ? "channel" : `local_port=${port} channel`; | ||
| return `request on ${where} ${channel} corr ${corr} timed out after ${ms}ms`; | ||
| } | ||
| private routeClosedDuringOpen(): SubcCallError { | ||
@@ -773,0 +790,0 @@ return new SubcCallError("not_sent", "route was closed before route.open completed", "route_closed"); |
+9
-0
@@ -67,2 +67,11 @@ // A pull-based buffered wrapper over a node TCP socket. Node sockets are | ||
| /** | ||
| * The OS-assigned local TCP port of this connection, or null if not yet | ||
| * connected/closed. Used to correlate a client-side timeout with a specific | ||
| * socket in a packet capture when diagnosing reply-delivery issues. | ||
| */ | ||
| localPort(): number | null { | ||
| return this.sock.localPort ?? null; | ||
| } | ||
| static connect(host: string, port: number, deadlineMs: number): Promise<SubcSocket> { | ||
@@ -69,0 +78,0 @@ return new Promise((resolve, reject) => { |
98522
1.41%2479
0.98%