@crouton-kit/crouter 0.3.60 → 0.3.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin-views/canvas/core.mjs +2 -1
- package/dist/clients/attach/__tests__/context-message.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/context-message.test.js +46 -0
- package/dist/clients/attach/__tests__/transport-relay.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/transport-relay.test.js +176 -0
- package/dist/clients/attach/attach-cmd.js +747 -743
- package/dist/clients/attach/canvas-panels.d.ts +4 -4
- package/dist/clients/attach/canvas-panels.js +108 -37
- package/dist/clients/attach/chat-view.js +11 -0
- package/dist/clients/attach/context-message.d.ts +3 -1
- package/dist/clients/attach/context-message.js +30 -21
- package/dist/clients/attach/graph-overlay.d.ts +8 -1
- package/dist/clients/attach/graph-overlay.js +201 -22
- package/dist/clients/attach/transport-relay.d.ts +51 -0
- package/dist/clients/attach/transport-relay.js +196 -0
- package/dist/clients/attach/transport-socket.d.ts +29 -0
- package/dist/clients/attach/transport-socket.js +164 -0
- package/dist/clients/attach/transport.d.ts +32 -0
- package/dist/clients/attach/transport.js +26 -0
- package/dist/clients/attach/view-socket.d.ts +40 -44
- package/dist/clients/attach/view-socket.js +94 -197
- package/dist/clients/web/server.js +1 -0
- package/dist/commands/memory/delete.js +3 -3
- package/dist/commands/memory/origin.js +1 -1
- package/dist/commands/memory/read.js +3 -3
- package/dist/commands/memory/shared.d.ts +8 -6
- package/dist/commands/memory/shared.js +23 -9
- package/dist/commands/memory/write.js +3 -3
- package/dist/commands/node-worktree.js +9 -9
- package/dist/commands/node.js +71 -23
- package/dist/commands/sys/doctor.js +1 -0
- package/dist/core/__tests__/broker-fork-seam.test.js +17 -2
- package/dist/core/__tests__/broker-snapshot-history.test.js +12 -2
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +79 -0
- package/dist/core/__tests__/context-intro.test.js +11 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +9 -4
- package/dist/core/__tests__/fixtures/fake-engine.js +12 -4
- package/dist/core/__tests__/kickoff.test.js +21 -0
- package/dist/core/__tests__/spawn-root.test.js +60 -0
- package/dist/core/__tests__/worktree.test.js +24 -1
- package/dist/core/canvas/browse/app.d.ts +2 -0
- package/dist/core/canvas/browse/app.js +83 -48
- package/dist/core/canvas/db.js +3 -2
- package/dist/core/canvas/index.d.ts +1 -0
- package/dist/core/canvas/index.js +1 -0
- package/dist/core/canvas/nav-model.d.ts +5 -3
- package/dist/core/canvas/nav-model.js +7 -6
- package/dist/core/canvas/paths.d.ts +8 -0
- package/dist/core/canvas/paths.js +10 -0
- package/dist/core/canvas/render.d.ts +6 -0
- package/dist/core/canvas/render.js +117 -20
- package/dist/core/canvas/source.d.ts +26 -0
- package/dist/core/canvas/source.js +29 -0
- package/dist/core/canvas/status-glyph.js +1 -0
- package/dist/core/canvas/types.d.ts +4 -3
- package/dist/core/config.d.ts +18 -2
- package/dist/core/config.js +88 -10
- package/dist/core/fault-classifier.d.ts +2 -2
- package/dist/core/memory-resolver.d.ts +9 -6
- package/dist/core/memory-resolver.js +28 -4
- package/dist/core/profiles/__tests__/root-profile.test.d.ts +1 -0
- package/dist/core/profiles/__tests__/root-profile.test.js +69 -0
- package/dist/core/profiles/manifest.d.ts +2 -0
- package/dist/core/profiles/manifest.js +40 -7
- package/dist/core/profiles/select.d.ts +4 -4
- package/dist/core/profiles/select.js +13 -13
- package/dist/core/runtime/__tests__/spawn-env.test.d.ts +1 -0
- package/dist/core/runtime/__tests__/spawn-env.test.js +268 -0
- package/dist/core/runtime/bearings.d.ts +5 -4
- package/dist/core/runtime/bearings.js +12 -6
- package/dist/core/runtime/broker.js +59 -7
- package/dist/core/runtime/fault-recovery.js +4 -0
- package/dist/core/runtime/front-door.js +1 -1
- package/dist/core/runtime/host.js +43 -3
- package/dist/core/runtime/kickoff.js +6 -0
- package/dist/core/runtime/launch.d.ts +6 -1
- package/dist/core/runtime/launch.js +7 -1
- package/dist/core/runtime/nodes.d.ts +7 -6
- package/dist/core/runtime/nodes.js +3 -3
- package/dist/core/runtime/situational-context.d.ts +12 -0
- package/dist/core/runtime/situational-context.js +48 -0
- package/dist/core/runtime/spawn-env.d.ts +27 -0
- package/dist/core/runtime/spawn-env.js +169 -0
- package/dist/core/runtime/spawn.d.ts +16 -7
- package/dist/core/runtime/spawn.js +8 -2
- package/dist/core/substrate/session-cache.js +7 -1
- package/dist/core/substrate/subject.d.ts +1 -1
- package/dist/core/view/remote-canvas-target.d.ts +3 -0
- package/dist/core/view/remote-canvas-target.js +25 -0
- package/dist/core/worktree.d.ts +4 -4
- package/dist/core/worktree.js +41 -19
- package/dist/daemon/crtrd.js +32 -0
- package/dist/daemon/predicate-eval.js +12 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/pi-extensions/canvas-bash-valve.d.ts +2 -0
- package/dist/pi-extensions/canvas-bash-valve.js +238 -0
- package/dist/pi-extensions/canvas-inbox-watcher.d.ts +12 -0
- package/dist/pi-extensions/canvas-inbox-watcher.js +41 -5
- package/dist/types.d.ts +32 -0
- package/dist/types.js +7 -0
- package/dist/web-client/apple-touch-icon.png +0 -0
- package/dist/web-client/assets/index-BnmSLNLa.css +2 -0
- package/dist/web-client/assets/index-BvzxXXGU.js +77 -0
- package/dist/web-client/assets/workbox-window.prod.es5-Bd17z0YL.js +2 -0
- package/dist/web-client/icon-192.png +0 -0
- package/dist/web-client/icon-512.png +0 -0
- package/dist/web-client/icon-maskable-512.png +0 -0
- package/dist/web-client/index.html +6 -3
- package/dist/web-client/manifest.webmanifest +1 -0
- package/dist/web-client/sw.js +1 -0
- package/docs/public-api.md +5 -3
- package/package.json +4 -3
- package/dist/web-client/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
- package/dist/web-client/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
- package/dist/web-client/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
- package/dist/web-client/assets/index-72YQdji_.js +0 -77
- package/dist/web-client/assets/index-bDhImjJy.css +0 -2
- package/dist/web-client/assets/instrument-sans-latin-ext-wght-normal-B5bTHO_g.woff2 +0 -0
- package/dist/web-client/assets/instrument-sans-latin-wght-normal-BbzFLZTg.woff2 +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// transport-socket.ts — the LOCAL unix-socket Transport. This is the ORIGINAL
|
|
2
|
+
// `ViewSocketClient` wire logic (node:net + the bounded FrameDecoder),
|
|
3
|
+
// unchanged in behavior, extracted behind the `Transport` interface so
|
|
4
|
+
// ViewSocketClient can also ride a `RelayTransport` (transport-relay.ts).
|
|
5
|
+
//
|
|
6
|
+
// Connects to a node's ALREADY-running headless broker over its unix socket
|
|
7
|
+
// (`nodeDir(id)/view.sock`). NEVER spawns pi, NEVER opens the session
|
|
8
|
+
// `.jsonl` — if the broker is not running, connect fails with a
|
|
9
|
+
// BrokerUnavailableError.
|
|
10
|
+
import { EventEmitter } from 'node:events';
|
|
11
|
+
import { createConnection } from 'node:net';
|
|
12
|
+
import { viewSocketPath } from '../../core/canvas/paths.js';
|
|
13
|
+
import { classify } from '../../core/fault-classifier.js';
|
|
14
|
+
import { clearFault, recordFault } from '../../core/runtime/fault.js';
|
|
15
|
+
import { CLIENT_READ_CAPS, encodeFrame, FrameDecoder, FrameOverflowError, } from '../../core/runtime/broker-protocol.js';
|
|
16
|
+
import { BrokerUnavailableError } from './transport.js';
|
|
17
|
+
export class SocketTransport extends EventEmitter {
|
|
18
|
+
nodeId;
|
|
19
|
+
socket;
|
|
20
|
+
decoder = new FrameDecoder(CLIENT_READ_CAPS);
|
|
21
|
+
closeEmitted = false;
|
|
22
|
+
/** One redial fault line per reconnect EPISODE, not per attempt — see
|
|
23
|
+
* {@link recordSocketFault}. */
|
|
24
|
+
redialFaultRecorded = false;
|
|
25
|
+
constructor(nodeId) {
|
|
26
|
+
super();
|
|
27
|
+
this.nodeId = nodeId;
|
|
28
|
+
}
|
|
29
|
+
/** The broker binds `viewSocketPath(id)` — resolve it the same way. */
|
|
30
|
+
get socketPath() {
|
|
31
|
+
return viewSocketPath(this.nodeId);
|
|
32
|
+
}
|
|
33
|
+
recordSocketFault(op, err) {
|
|
34
|
+
if (op === 'redial') {
|
|
35
|
+
if (this.redialFaultRecorded)
|
|
36
|
+
return;
|
|
37
|
+
this.redialFaultRecorded = true;
|
|
38
|
+
}
|
|
39
|
+
const classified = classify('viewer↔broker', err);
|
|
40
|
+
recordFault(this.nodeId, {
|
|
41
|
+
link: 'viewer↔broker',
|
|
42
|
+
op,
|
|
43
|
+
kind: classified.kind,
|
|
44
|
+
retry: op === 'redial'
|
|
45
|
+
? { disposition: 'auto', by: 'client' }
|
|
46
|
+
: { disposition: 'manual' },
|
|
47
|
+
message: err instanceof Error ? err.message : String(err),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
connect() {
|
|
51
|
+
const socket = createConnection(this.socketPath);
|
|
52
|
+
this.socket = socket;
|
|
53
|
+
socket.on('connect', () => {
|
|
54
|
+
this.redialFaultRecorded = false;
|
|
55
|
+
clearFault(this.nodeId, { link: 'viewer↔broker' });
|
|
56
|
+
this.emit('connect');
|
|
57
|
+
});
|
|
58
|
+
socket.on('data', (chunk) => this.onData(chunk));
|
|
59
|
+
socket.on('error', (err) => this.onError(err, 'connect'));
|
|
60
|
+
socket.on('close', () => this.onClose());
|
|
61
|
+
}
|
|
62
|
+
/** Re-establish the socket after a broker exit, on the SAME stable
|
|
63
|
+
* `view.sock` path. Installs a FRESH FrameDecoder (a half-frame from the
|
|
64
|
+
* dead stream must not corrupt the new one). */
|
|
65
|
+
redial() {
|
|
66
|
+
this.destroy();
|
|
67
|
+
this.decoder = new FrameDecoder(CLIENT_READ_CAPS);
|
|
68
|
+
this.closeEmitted = false;
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
const socket = createConnection(this.socketPath);
|
|
71
|
+
this.socket = socket;
|
|
72
|
+
let settled = false;
|
|
73
|
+
socket.on('connect', () => {
|
|
74
|
+
if (settled)
|
|
75
|
+
return;
|
|
76
|
+
settled = true;
|
|
77
|
+
this.redialFaultRecorded = false;
|
|
78
|
+
clearFault(this.nodeId, { link: 'viewer↔broker' });
|
|
79
|
+
this.emit('connect');
|
|
80
|
+
resolve();
|
|
81
|
+
});
|
|
82
|
+
socket.on('data', (chunk) => this.onData(chunk));
|
|
83
|
+
socket.on('error', (err) => {
|
|
84
|
+
if (settled) {
|
|
85
|
+
this.onError(err, 'redial');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
settled = true;
|
|
89
|
+
this.recordSocketFault('redial', err);
|
|
90
|
+
try {
|
|
91
|
+
socket.destroy();
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
/* ignore */
|
|
95
|
+
}
|
|
96
|
+
reject(err);
|
|
97
|
+
});
|
|
98
|
+
socket.on('close', () => this.onClose());
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
send(frame) {
|
|
102
|
+
const sock = this.socket;
|
|
103
|
+
if (sock === undefined || sock.destroyed)
|
|
104
|
+
return;
|
|
105
|
+
try {
|
|
106
|
+
sock.write(encodeFrame(frame));
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
/* dead socket — 'close' will fire and drive teardown */
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
destroy() {
|
|
113
|
+
const sock = this.socket;
|
|
114
|
+
if (sock !== undefined && !sock.destroyed) {
|
|
115
|
+
try {
|
|
116
|
+
sock.destroy();
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
/* ignore */
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
onData(chunk) {
|
|
124
|
+
let frames;
|
|
125
|
+
try {
|
|
126
|
+
frames = this.decoder.push(chunk);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
// A broker that somehow emits an oversized frame (or a corrupt stream):
|
|
130
|
+
// clean error + drop, never a crash. The 'close' that follows destroy()
|
|
131
|
+
// is what rejects any in-flight ViewSocketClient requests.
|
|
132
|
+
const msg = err instanceof FrameOverflowError
|
|
133
|
+
? `broker sent an oversized frame (${err.message}) — disconnecting`
|
|
134
|
+
: `failed to decode a broker frame: ${String(err)}`;
|
|
135
|
+
this.emitError(new Error(msg));
|
|
136
|
+
this.destroy();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
for (const raw of frames)
|
|
140
|
+
this.emit('frame', raw);
|
|
141
|
+
}
|
|
142
|
+
onError(err, op) {
|
|
143
|
+
this.recordSocketFault(op, err);
|
|
144
|
+
if (err.code === 'ECONNREFUSED' || err.code === 'ENOENT') {
|
|
145
|
+
this.emitError(new BrokerUnavailableError(this.nodeId));
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.emitError(new Error(`view socket error: ${err.message}`));
|
|
149
|
+
}
|
|
150
|
+
// 'close' typically follows 'error'; teardown converges there.
|
|
151
|
+
}
|
|
152
|
+
onClose() {
|
|
153
|
+
if (this.closeEmitted)
|
|
154
|
+
return;
|
|
155
|
+
this.closeEmitted = true;
|
|
156
|
+
this.emit('close');
|
|
157
|
+
}
|
|
158
|
+
/** Emit `error` only when a listener exists — a bare EventEmitter `error`
|
|
159
|
+
* with no listener throws, and this transport must never throw uncaught. */
|
|
160
|
+
emitError(err) {
|
|
161
|
+
if (this.listenerCount('error') > 0)
|
|
162
|
+
this.emit('error', err);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { EventEmitter } from 'node:events';
|
|
2
|
+
import type { BrokerToClient, ClientToBroker } from '../../core/runtime/broker-protocol.js';
|
|
3
|
+
export interface Transport extends EventEmitter {
|
|
4
|
+
/** Open the connection. Emits 'connect' on success, 'error' on a fatal
|
|
5
|
+
* connect-time failure. Call once. */
|
|
6
|
+
connect(): void;
|
|
7
|
+
/** Re-establish the connection to the SAME target after a drop. Resolves on
|
|
8
|
+
* a fresh 'connect'; rejects on a retryable dial failure (the caller's
|
|
9
|
+
* reconnect supervisor decides whether to try again). */
|
|
10
|
+
redial(): Promise<void>;
|
|
11
|
+
/** Encode + send one client→broker frame. No-op on a dead/absent connection;
|
|
12
|
+
* never throws. */
|
|
13
|
+
send(frame: ClientToBroker): void;
|
|
14
|
+
/** Tear down the connection. 'close' fires if it has not already. */
|
|
15
|
+
destroy(): void;
|
|
16
|
+
on(event: 'connect', listener: () => void): this;
|
|
17
|
+
on(event: 'frame', listener: (frame: BrokerToClient) => void): this;
|
|
18
|
+
on(event: 'close', listener: () => void): this;
|
|
19
|
+
on(event: 'error', listener: (err: Error) => void): this;
|
|
20
|
+
once(event: 'connect', listener: () => void): this;
|
|
21
|
+
once(event: 'error', listener: (err: Error) => void): this;
|
|
22
|
+
off(event: 'connect', listener: () => void): this;
|
|
23
|
+
off(event: 'error', listener: (err: Error) => void): this;
|
|
24
|
+
}
|
|
25
|
+
/** Surfaced when a Transport's target has no reachable broker — locally, no
|
|
26
|
+
* socket file / nothing listening; remotely, a relay close signaling "no
|
|
27
|
+
* broker for this node". The command catches this to exit non-zero with a
|
|
28
|
+
* focus/revive hint — same meaning regardless of which Transport raised it. */
|
|
29
|
+
export declare class BrokerUnavailableError extends Error {
|
|
30
|
+
readonly nodeId: string;
|
|
31
|
+
constructor(nodeId: string);
|
|
32
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// transport.ts — the Transport seam: ViewSocketClient's broker I/O, abstracted
|
|
2
|
+
// so the SAME correlation/pending/close logic can ride either the LOCAL unix
|
|
3
|
+
// socket (SocketTransport, transport-socket.ts) or a relayed WebSocket to a
|
|
4
|
+
// remote broker (RelayTransport, transport-relay.ts). Both speak the same
|
|
5
|
+
// broker-protocol JSON frames; only how bytes cross the wire differs.
|
|
6
|
+
//
|
|
7
|
+
// A Transport is a pure duplex frame pump — connect (dial), redial (re-dial
|
|
8
|
+
// the SAME target after a drop), send (one client→broker frame), destroy
|
|
9
|
+
// (tear down). It emits 'connect' once the peer is reachable, one 'frame' per
|
|
10
|
+
// decoded broker→client frame, 'error' for a surfaced problem (including a
|
|
11
|
+
// {@link BrokerUnavailableError} when the target has no reachable broker),
|
|
12
|
+
// and 'close' exactly once when the connection is gone. Everything ABOVE this
|
|
13
|
+
// seam — request correlation, the pending map, redial-vs-give-up policy — is
|
|
14
|
+
// ViewSocketClient's job and never moves into a Transport implementation.
|
|
15
|
+
/** Surfaced when a Transport's target has no reachable broker — locally, no
|
|
16
|
+
* socket file / nothing listening; remotely, a relay close signaling "no
|
|
17
|
+
* broker for this node". The command catches this to exit non-zero with a
|
|
18
|
+
* focus/revive hint — same meaning regardless of which Transport raised it. */
|
|
19
|
+
export class BrokerUnavailableError extends Error {
|
|
20
|
+
nodeId;
|
|
21
|
+
constructor(nodeId) {
|
|
22
|
+
super(`node ${nodeId} has no running broker — focus or revive it first`);
|
|
23
|
+
this.nodeId = nodeId;
|
|
24
|
+
this.name = 'BrokerUnavailableError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import type { NodeRow } from '../../core/canvas/types.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { BrokerDataFrame, BrokerToClient, ClientToBroker, DequeueFrame, GetSettingsFrame, GetTreeFrame, ListModelsFrame, ListScopedModelsFrame, ListSessionsFrame } from '../../core/runtime/broker-protocol.js';
|
|
4
|
+
import { type Transport } from './transport.js';
|
|
5
|
+
export { BrokerUnavailableError, type Transport } from './transport.js';
|
|
6
|
+
export { SocketTransport } from './transport-socket.js';
|
|
7
|
+
export { RelayTransport, type RelayTarget } from './transport-relay.js';
|
|
4
8
|
/** A correlated read-op (or `dequeue`) request MINUS the client-chosen `id` —
|
|
5
9
|
* {@link ViewSocketClient.request} mints the `id`, sends the frame, and resolves
|
|
6
10
|
* with the matching `data` reply. The picker/operator code builds these; the
|
|
7
11
|
* socket owns the correlation token so callers never hand-roll one. */
|
|
8
12
|
export type ReadOpRequest = Omit<ListModelsFrame, 'id'> | Omit<ListSessionsFrame, 'id'> | Omit<GetTreeFrame, 'id'> | Omit<GetSettingsFrame, 'id'> | Omit<ListScopedModelsFrame, 'id'> | Omit<DequeueFrame, 'id'>;
|
|
9
|
-
/** Surfaced when the node has no reachable broker at connect time (no socket
|
|
10
|
-
* file, or a stale socket with nothing listening). The command catches this to
|
|
11
|
-
* exit non-zero with a focus/revive hint. */
|
|
12
13
|
/** The reconnect supervisor's give-up predicate (extracted pure so it is
|
|
13
14
|
* testable without a socket or TUI). After a broker close the viewer KEEPS
|
|
14
|
-
* re-dialing the same
|
|
15
|
+
* re-dialing the same target while the node is still alive — a yield
|
|
15
16
|
* leaves `status='active'` (intent='refresh') and the daemon revives a fresh
|
|
16
17
|
* broker on the same path. It gives up only when the node is genuinely gone:
|
|
17
18
|
* a terminal status (done/dead/canceled) or a reaped row (null). `idle` is NOT
|
|
18
19
|
* terminal — an idle-release node revives on its next inbox wake, so keep
|
|
19
20
|
* trying (the supervisor waits at a relaxed cadence for as long as the row
|
|
20
|
-
* stays live).
|
|
21
|
+
* stays live). Local canvas rows only — a remote target has no local row, so
|
|
22
|
+
* callers driving a remote attach must not consult this (see attach-cmd.ts). */
|
|
21
23
|
export declare function reconnectShouldGiveUp(row: NodeRow | null): boolean;
|
|
22
|
-
export declare class BrokerUnavailableError extends Error {
|
|
23
|
-
readonly nodeId: string;
|
|
24
|
-
constructor(nodeId: string);
|
|
25
|
-
}
|
|
26
24
|
/** The plan-fixed interface (Wave 3): `connect()`, `on('frame', …)`,
|
|
27
25
|
* `send(frame)`, `on('close', …)`, plus `connect`/`error` events. */
|
|
28
26
|
export interface ViewSocketClient {
|
|
@@ -39,52 +37,50 @@ export interface ViewSocketClient {
|
|
|
39
37
|
}
|
|
40
38
|
export declare class ViewSocketClient extends EventEmitter {
|
|
41
39
|
private readonly nodeId;
|
|
42
|
-
private
|
|
43
|
-
private decoder;
|
|
40
|
+
private readonly transport;
|
|
44
41
|
private closeEmitted;
|
|
42
|
+
/** Tracks live connect↔close state so {@link request} can fail fast on a
|
|
43
|
+
* dead/absent connection rather than parking a promise the reply will
|
|
44
|
+
* never reach (transport-agnostic mirror of the old socket.destroyed
|
|
45
|
+
* check). A post-close request lands here too — {@link rejectAllPending}
|
|
46
|
+
* already ran by the time this flips false. */
|
|
47
|
+
private connected;
|
|
48
|
+
/** The last transport-level error message, so a close can reject any
|
|
49
|
+
* in-flight requests with the precise reason (e.g. an oversized-frame
|
|
50
|
+
* overflow) instead of a generic one. Cleared on every fresh connect. */
|
|
51
|
+
private lastTransportError;
|
|
45
52
|
/** In-flight correlated read-ops, keyed by the `id` minted in {@link request}.
|
|
46
53
|
* Resolved by the matching `data` frame / rejected by the matching `error`
|
|
47
|
-
* frame in {@link
|
|
54
|
+
* frame in {@link onFrame}, the request's timeout, or transport teardown. */
|
|
48
55
|
private pending;
|
|
49
|
-
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
|
|
54
|
-
private redialFaultRecorded;
|
|
55
|
-
constructor(nodeId: string);
|
|
56
|
-
private recordSocketFault;
|
|
56
|
+
/** `transport` defaults to a {@link SocketTransport} against this node's
|
|
57
|
+
* local `view.sock` — today's behavior, unchanged. Pass a
|
|
58
|
+
* {@link RelayTransport} to attach a remote node instead; every method
|
|
59
|
+
* below behaves identically either way. */
|
|
60
|
+
constructor(nodeId: string, transport?: Transport);
|
|
57
61
|
/** Issue a correlated read-op and resolve with the broker's `data` reply (or
|
|
58
|
-
* reject on the correlated `error`, a timeout, or
|
|
59
|
-
* `id`, sends `{...frame, id}`, and parks a resolver consumed by
|
|
60
|
-
* The reply is narrowed by the caller on its `kind`. */
|
|
62
|
+
* reject on the correlated `error`, a timeout, or transport teardown). Mints
|
|
63
|
+
* the `id`, sends `{...frame, id}`, and parks a resolver consumed by
|
|
64
|
+
* {@link onFrame}. The reply is narrowed by the caller on its `kind`. */
|
|
61
65
|
request(frame: ReadOpRequest): Promise<BrokerDataFrame>;
|
|
62
|
-
/** Reject + clear every in-flight request (
|
|
63
|
-
* picker fetch never hangs past the connection it rode on. */
|
|
66
|
+
/** Reject + clear every in-flight request (connection gone / decode error)
|
|
67
|
+
* so a picker fetch never hangs past the connection it rode on. */
|
|
64
68
|
private rejectAllPending;
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
* `error` (a {@link BrokerUnavailableError} for ECONNREFUSED/ENOENT) on
|
|
69
|
-
* failure. Idempotent guard is the caller's job — call once. */
|
|
69
|
+
/** Open the connection. Emits `connect` on success or `error` (a
|
|
70
|
+
* {@link BrokerUnavailableError} when the target has no reachable broker)
|
|
71
|
+
* on failure. Idempotent guard is the caller's job — call once. */
|
|
70
72
|
connect(): void;
|
|
71
|
-
/** Re-establish the
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* guard, then re-dials. Resolves on `connect` (caller re-sends `hello`);
|
|
75
|
-
* rejects on the dial's `error` (ECONNREFUSED while the new broker is
|
|
76
|
-
* mid-boot, or ENOENT before it re-binds the socket) — both retryable. A
|
|
77
|
-
* post-connect error flows through the normal `onError`→`close` path. */
|
|
73
|
+
/** Re-establish the connection after a broker exit (a yield→revive cycle),
|
|
74
|
+
* to the SAME target. Resolves on `connect` (caller re-sends `hello`);
|
|
75
|
+
* rejects on a retryable dial failure. */
|
|
78
76
|
redial(): Promise<void>;
|
|
79
|
-
/** Encode +
|
|
80
|
-
*
|
|
77
|
+
/** Encode + send one client→broker frame. No-op on a dead/absent
|
|
78
|
+
* connection; never throws. */
|
|
81
79
|
send(frame: ClientToBroker): void;
|
|
82
|
-
/** Detach:
|
|
80
|
+
/** Detach: tear down the transport. `close` fires → the caller tears down. */
|
|
83
81
|
close(): void;
|
|
84
|
-
private
|
|
85
|
-
private onError;
|
|
82
|
+
private onFrame;
|
|
86
83
|
private onClose;
|
|
87
|
-
private destroy;
|
|
88
84
|
/** Emit `error` only when a listener exists — a bare EventEmitter `error`
|
|
89
85
|
* with no listener throws, and this client must never throw uncaught. */
|
|
90
86
|
private emitError;
|