@actana/sdk 0.2.2
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/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/core-client.d.ts +616 -0
- package/dist/core-client.d.ts.map +1 -0
- package/dist/core-client.js +1036 -0
- package/dist/core-client.js.map +1 -0
- package/dist/core-link-cursor-storage.d.ts +32 -0
- package/dist/core-link-cursor-storage.d.ts.map +1 -0
- package/dist/core-link-cursor-storage.js +66 -0
- package/dist/core-link-cursor-storage.js.map +1 -0
- package/dist/core-link-frames.d.ts +1123 -0
- package/dist/core-link-frames.d.ts.map +1 -0
- package/dist/core-link-frames.js +349 -0
- package/dist/core-link-frames.js.map +1 -0
- package/dist/core-link-socket.d.ts +54 -0
- package/dist/core-link-socket.d.ts.map +1 -0
- package/dist/core-link-socket.js +74 -0
- package/dist/core-link-socket.js.map +1 -0
- package/dist/core-link-transport.d.ts +177 -0
- package/dist/core-link-transport.d.ts.map +1 -0
- package/dist/core-link-transport.js +432 -0
- package/dist/core-link-transport.js.map +1 -0
- package/dist/core-registration-blob.d.ts +52 -0
- package/dist/core-registration-blob.d.ts.map +1 -0
- package/dist/core-registration-blob.js +61 -0
- package/dist/core-registration-blob.js.map +1 -0
- package/dist/core-session.d.ts +321 -0
- package/dist/core-session.d.ts.map +1 -0
- package/dist/core-session.js +660 -0
- package/dist/core-session.js.map +1 -0
- package/dist/durable-core-client.d.ts +172 -0
- package/dist/durable-core-client.d.ts.map +1 -0
- package/dist/durable-core-client.js +264 -0
- package/dist/durable-core-client.js.map +1 -0
- package/dist/terminal-screen.d.ts +139 -0
- package/dist/terminal-screen.d.ts.map +1 -0
- package/dist/terminal-screen.js +807 -0
- package/dist/terminal-screen.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { CoreLinkEvent, CoreLinkRequestFrame, CoreLinkResponseFrame, CoreLinkStreamFrame } from "./core-link-frames.ts";
|
|
2
|
+
import type { CoreLinkSocketFactory } from "./core-link-socket.ts";
|
|
3
|
+
/** The Core's opening frame on every connection — protocol version, capability. */
|
|
4
|
+
export type CoreLinkReadyFrame = Extract<CoreLinkResponseFrame, {
|
|
5
|
+
type: "ready";
|
|
6
|
+
}>;
|
|
7
|
+
/** One PTY's bytes. Unsolicited; keyed by `ptyId`; carries a `seq`. */
|
|
8
|
+
export type CoreLinkDataFrame = Extract<CoreLinkStreamFrame, {
|
|
9
|
+
type: "data";
|
|
10
|
+
}>;
|
|
11
|
+
/** A PTY's exit. Unsolicited; keyed by `ptyId`. */
|
|
12
|
+
export type CoreLinkExitFrame = Extract<CoreLinkStreamFrame, {
|
|
13
|
+
type: "exit";
|
|
14
|
+
}>;
|
|
15
|
+
/** The Core accepted this connection's bearer. */
|
|
16
|
+
export type CoreLinkAuthOkFrame = Extract<CoreLinkResponseFrame, {
|
|
17
|
+
type: "authOk";
|
|
18
|
+
}>;
|
|
19
|
+
/** The Core refused this connection's bearer, and is about to close the socket. */
|
|
20
|
+
export type CoreLinkAuthErrorFrame = Extract<CoreLinkResponseFrame, {
|
|
21
|
+
type: "authError";
|
|
22
|
+
}>;
|
|
23
|
+
/** Why the Core refused a bearer. */
|
|
24
|
+
export type CoreLinkAuthErrorReason = CoreLinkAuthErrorFrame["reason"];
|
|
25
|
+
/**
|
|
26
|
+
* What a transport tells its owner. Every one is optional and none may throw
|
|
27
|
+
* anything the transport is expected to survive — a listener's failure must not
|
|
28
|
+
* take a connection down, so each is called inside a guard.
|
|
29
|
+
*/
|
|
30
|
+
export type CoreLinkTransportHandlers = {
|
|
31
|
+
/** The socket is open. Nothing has been authenticated yet. */
|
|
32
|
+
onOpen?: () => void;
|
|
33
|
+
/**
|
|
34
|
+
* The transport may now be written to: the socket is open and, when a bearer
|
|
35
|
+
* was configured, the Core has accepted it. This is where an owner sends the
|
|
36
|
+
* frames a fresh connection owes — `reclaim`, `subscribe`, a re-subscription
|
|
37
|
+
* — and they go out ahead of whatever the owner had queued.
|
|
38
|
+
*/
|
|
39
|
+
onWritable?: () => void;
|
|
40
|
+
/** Frame one, on every connection (see this module's header). */
|
|
41
|
+
onReady?: (frame: CoreLinkReadyFrame) => void;
|
|
42
|
+
/** One PTY's bytes, parsed once, here. */
|
|
43
|
+
onData?: (frame: CoreLinkDataFrame) => void;
|
|
44
|
+
/** One PTY's exit, parsed once, here. */
|
|
45
|
+
onExit?: (frame: CoreLinkExitFrame) => void;
|
|
46
|
+
/** One event off the Core's monotonic log — replayed or live, same frame. */
|
|
47
|
+
onEvent?: (event: CoreLinkEvent) => void;
|
|
48
|
+
/** End of the `subscribe` replay tail; live push resumes after it. */
|
|
49
|
+
onEventsReplayed?: (lastEventId: number) => void;
|
|
50
|
+
onAuthOk?: (frame: CoreLinkAuthOkFrame) => void;
|
|
51
|
+
onAuthError?: (reason: CoreLinkAuthErrorReason) => void;
|
|
52
|
+
/**
|
|
53
|
+
* The socket is gone, with the reason the `error` event carried if it carried
|
|
54
|
+
* one. In-flight requests have already been rejected by the time this runs.
|
|
55
|
+
*/
|
|
56
|
+
onClose?: (reason?: string) => void;
|
|
57
|
+
};
|
|
58
|
+
export type CoreLinkHeartbeatOptions = {
|
|
59
|
+
/** Ping cadence. Default {@link DEFAULT_HEARTBEAT_INTERVAL_MS}. */
|
|
60
|
+
intervalMs?: number;
|
|
61
|
+
/** Declare the peer dead after this long with no frame. Default {@link DEFAULT_HEARTBEAT_TIMEOUT_MS}. */
|
|
62
|
+
timeoutMs?: number;
|
|
63
|
+
};
|
|
64
|
+
export type CoreLinkTransportOptions = {
|
|
65
|
+
/** The URL to dial. Reported back on errors; the factory is what opens it. */
|
|
66
|
+
url: string;
|
|
67
|
+
/** How to open the socket. See {@link CoreLinkSocketFactory}. */
|
|
68
|
+
createSocket: CoreLinkSocketFactory;
|
|
69
|
+
/**
|
|
70
|
+
* The signed bearer `{coreId, exp, sig}` from the registration blob (ADR
|
|
71
|
+
* 0002), presented in the `auth` frame the instant the socket opens. Every
|
|
72
|
+
* real Core requires one — there is no trusted transport left to omit it on
|
|
73
|
+
* (ADR 0010). Omitted, the transport is writable as soon as the socket opens,
|
|
74
|
+
* which is the loopback rig and the test.
|
|
75
|
+
*/
|
|
76
|
+
bearer?: string | null;
|
|
77
|
+
/**
|
|
78
|
+
* Arm the heartbeat on this connection. Off by default, and deliberately so:
|
|
79
|
+
* a one-shot client that connects, asks and exits has nothing to keep alive,
|
|
80
|
+
* and the durable entry point turns it on because a link that must survive an
|
|
81
|
+
* idle hour is the case it exists for (#129 D6).
|
|
82
|
+
*
|
|
83
|
+
* Only a socket that can actually send a ping participates — see
|
|
84
|
+
* {@link CoreLinkSocket.ping}.
|
|
85
|
+
*/
|
|
86
|
+
heartbeat?: CoreLinkHeartbeatOptions | false;
|
|
87
|
+
handlers?: CoreLinkTransportHandlers;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Heartbeat cadence. A remote Core's core link runs over the open internet, so
|
|
91
|
+
* it crosses NATs and stateful firewalls that silently drop idle flows — an
|
|
92
|
+
* agent sitting at its prompt sends nothing for minutes, which is exactly the
|
|
93
|
+
* traffic pattern those boxes reap. Without a heartbeat the drop is invisible:
|
|
94
|
+
* TCP has no keepalive here, so the client keeps believing it is connected,
|
|
95
|
+
* keystrokes are written into a dead socket, and requests hang for their full
|
|
96
|
+
* timeout while the Core happily streams output nobody receives. Pinging every
|
|
97
|
+
* 15s keeps the flow alive AND detects death within one window.
|
|
98
|
+
*/
|
|
99
|
+
export declare const DEFAULT_HEARTBEAT_INTERVAL_MS = 15000;
|
|
100
|
+
/** Declare the peer dead after this long with no frame of any kind (3 pings). */
|
|
101
|
+
export declare const DEFAULT_HEARTBEAT_TIMEOUT_MS = 45000;
|
|
102
|
+
/** The message a request rejects with when its socket died before the answer. */
|
|
103
|
+
export declare const CONNECTION_LOST_MESSAGE = "core-link connection lost";
|
|
104
|
+
/**
|
|
105
|
+
* One core-link connection. Constructed open-ended: the socket is dialed
|
|
106
|
+
* immediately, and the handlers passed in are the only way anything gets out of
|
|
107
|
+
* it. Not reusable — a transport is one socket, and a client that wants another
|
|
108
|
+
* builds another.
|
|
109
|
+
*/
|
|
110
|
+
export declare class CoreLinkTransport {
|
|
111
|
+
private readonly handlers;
|
|
112
|
+
private readonly bearer;
|
|
113
|
+
private readonly heartbeatOpts;
|
|
114
|
+
private socket;
|
|
115
|
+
private reqSeq;
|
|
116
|
+
private readonly pending;
|
|
117
|
+
private opened;
|
|
118
|
+
private authenticated;
|
|
119
|
+
private disposed;
|
|
120
|
+
/** The reason the socket died, off the `error` event, for the close report. */
|
|
121
|
+
private lastError;
|
|
122
|
+
private ready;
|
|
123
|
+
private heartbeatTimer;
|
|
124
|
+
/** Epoch ms of the last frame received (message OR pong) on this connection. */
|
|
125
|
+
private lastInboundAt;
|
|
126
|
+
/** Injectable clock so a fake-timer test can drive the heartbeat. */
|
|
127
|
+
private readonly now;
|
|
128
|
+
constructor(opts: CoreLinkTransportOptions);
|
|
129
|
+
private wire;
|
|
130
|
+
/** True while this connection can carry a frame — open, and authenticated if it must be. */
|
|
131
|
+
get writable(): boolean;
|
|
132
|
+
/** True once the Core has accepted this connection's bearer. */
|
|
133
|
+
get isAuthenticated(): boolean;
|
|
134
|
+
/** This connection's `ready` frame, or null before it lands. */
|
|
135
|
+
get readyFrame(): CoreLinkReadyFrame | null;
|
|
136
|
+
/**
|
|
137
|
+
* Send a request frame and resolve with the Core's answer — **frames both
|
|
138
|
+
* ways**, errors included, because a router forwarding on behalf of somebody
|
|
139
|
+
* else needs the frame rather than an exception (that is the Panel's shape,
|
|
140
|
+
* and `CoreClient` is what turns a failure frame into a rejection).
|
|
141
|
+
*
|
|
142
|
+
* The `reqId` on the frame passed in is ignored: this transport owns
|
|
143
|
+
* correlation on its own socket and returns the frame carrying the id it
|
|
144
|
+
* assigned. Rejects if the socket dies before the answer arrives; never times
|
|
145
|
+
* out on its own — a deadline is the caller's policy, and the caller above
|
|
146
|
+
* this one has one.
|
|
147
|
+
*/
|
|
148
|
+
request(frame: CoreLinkRequestFrame): Promise<CoreLinkResponseFrame>;
|
|
149
|
+
/**
|
|
150
|
+
* Send a frame and forget it. For the frames whose answer is a *stream* rather
|
|
151
|
+
* than a response — `subscribe`, whose reply is the replay tail and the
|
|
152
|
+
* `eventsReplayed` marker — and for the ones nothing waits on by design.
|
|
153
|
+
* Returns the assigned `reqId`, or null when the transport could not take it.
|
|
154
|
+
*/
|
|
155
|
+
send(frame: CoreLinkRequestFrame, reqIdPrefix?: string): string | null;
|
|
156
|
+
/** Hang up. Idempotent; fires no `onClose`, because this is not the Core going away. */
|
|
157
|
+
close(): void;
|
|
158
|
+
private nextReqId;
|
|
159
|
+
private write;
|
|
160
|
+
private onMessage;
|
|
161
|
+
private rejectAll;
|
|
162
|
+
/**
|
|
163
|
+
* Arm the heartbeat for a freshly opened socket. Only a transport that can
|
|
164
|
+
* actually send a ping participates; a socket without one is left alone
|
|
165
|
+
* rather than being torn down for failing to answer pings nobody sent.
|
|
166
|
+
*/
|
|
167
|
+
private startHeartbeat;
|
|
168
|
+
private stopHeartbeat;
|
|
169
|
+
/**
|
|
170
|
+
* Run an owner's callback. A listener that throws must not take the
|
|
171
|
+
* connection with it — half the callbacks here run inside a socket event, and
|
|
172
|
+
* an exception escaping one of those is an unhandled rejection at best and a
|
|
173
|
+
* connection stuck half-established at worst.
|
|
174
|
+
*/
|
|
175
|
+
private guard;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=core-link-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-link-transport.d.ts","sourceRoot":"","sources":["../src/core-link-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
// One core link, from the client's side of it: one socket, one Core.
|
|
2
|
+
//
|
|
3
|
+
// Extracted from the Panel's `PtyCoreLinkClient` (issue 153, #129 D1/D2/D6) —
|
|
4
|
+
// which was `packages/panel/src/server/core-link/client.ts`, deleted in #156 when
|
|
5
|
+
// the Panel moved onto this package, so this is where that code lives now.
|
|
6
|
+
// That class did four separable jobs at once — frame the wire, correlate
|
|
7
|
+
// requests, keep a link alive across drops, and expose a typed API. This module
|
|
8
|
+
// is the first of those two: **the machinery of one connection**, with no
|
|
9
|
+
// opinion about what happens when it ends.
|
|
10
|
+
//
|
|
11
|
+
// Everything that outlives a socket lives above it: `CoreClient` owns the typed
|
|
12
|
+
// methods and the outbound queue, and `DurableCoreClient` owns reconnection.
|
|
13
|
+
// One transport is one socket's whole life, which is what makes the difference
|
|
14
|
+
// between the two entry points a difference in *who re-opens*, rather than two
|
|
15
|
+
// implementations of a wire.
|
|
16
|
+
//
|
|
17
|
+
// Three things about this protocol shape the code below, and each has cost
|
|
18
|
+
// somebody a debugging session:
|
|
19
|
+
//
|
|
20
|
+
// 1. **The Core speaks first, unsolicited.** `ready` is frame one on every
|
|
21
|
+
// connection and answers no request — a transport that treats the first
|
|
22
|
+
// inbound message as a response to something it sent waits forever. It is
|
|
23
|
+
// surfaced as {@link CoreLinkTransportHandlers.onReady}.
|
|
24
|
+
// 2. **`auth` must be the first frame sent**, or the Core answers
|
|
25
|
+
// `not-authenticated` and closes the socket. So nothing else may go out on
|
|
26
|
+
// open: a subscribe, a reclaim, a caller's RPC all queue behind it, and the
|
|
27
|
+
// transport does not report itself {@link CoreLinkTransport.writable} until
|
|
28
|
+
// `authOk` lands.
|
|
29
|
+
// 3. **`data` and `exit` arrive unsolicited too**, carrying a `ptyId` and no
|
|
30
|
+
// `reqId`. They are surfaced as frames, parsed once, here — a caller that
|
|
31
|
+
// re-parses a raw message is a caller that will disagree with this one.
|
|
32
|
+
/**
|
|
33
|
+
* Heartbeat cadence. A remote Core's core link runs over the open internet, so
|
|
34
|
+
* it crosses NATs and stateful firewalls that silently drop idle flows — an
|
|
35
|
+
* agent sitting at its prompt sends nothing for minutes, which is exactly the
|
|
36
|
+
* traffic pattern those boxes reap. Without a heartbeat the drop is invisible:
|
|
37
|
+
* TCP has no keepalive here, so the client keeps believing it is connected,
|
|
38
|
+
* keystrokes are written into a dead socket, and requests hang for their full
|
|
39
|
+
* timeout while the Core happily streams output nobody receives. Pinging every
|
|
40
|
+
* 15s keeps the flow alive AND detects death within one window.
|
|
41
|
+
*/
|
|
42
|
+
export const DEFAULT_HEARTBEAT_INTERVAL_MS = 15_000;
|
|
43
|
+
/** Declare the peer dead after this long with no frame of any kind (3 pings). */
|
|
44
|
+
export const DEFAULT_HEARTBEAT_TIMEOUT_MS = 45_000;
|
|
45
|
+
/** The message a request rejects with when its socket died before the answer. */
|
|
46
|
+
export const CONNECTION_LOST_MESSAGE = "core-link connection lost";
|
|
47
|
+
/**
|
|
48
|
+
* One core-link connection. Constructed open-ended: the socket is dialed
|
|
49
|
+
* immediately, and the handlers passed in are the only way anything gets out of
|
|
50
|
+
* it. Not reusable — a transport is one socket, and a client that wants another
|
|
51
|
+
* builds another.
|
|
52
|
+
*/
|
|
53
|
+
export class CoreLinkTransport {
|
|
54
|
+
handlers;
|
|
55
|
+
bearer;
|
|
56
|
+
heartbeatOpts;
|
|
57
|
+
socket = null;
|
|
58
|
+
reqSeq = 0;
|
|
59
|
+
pending = new Map();
|
|
60
|
+
opened = false;
|
|
61
|
+
authenticated = false;
|
|
62
|
+
disposed = false;
|
|
63
|
+
/** The reason the socket died, off the `error` event, for the close report. */
|
|
64
|
+
lastError;
|
|
65
|
+
ready = null;
|
|
66
|
+
heartbeatTimer = null;
|
|
67
|
+
/** Epoch ms of the last frame received (message OR pong) on this connection. */
|
|
68
|
+
lastInboundAt = 0;
|
|
69
|
+
/** Injectable clock so a fake-timer test can drive the heartbeat. */
|
|
70
|
+
now = () => Date.now();
|
|
71
|
+
constructor(opts) {
|
|
72
|
+
this.handlers = opts.handlers ?? {};
|
|
73
|
+
this.bearer = opts.bearer ?? null;
|
|
74
|
+
this.heartbeatOpts = opts.heartbeat === false ? null : (opts.heartbeat ?? null);
|
|
75
|
+
let socket;
|
|
76
|
+
try {
|
|
77
|
+
socket = opts.createSocket(opts.url);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
// A factory that throws — a malformed URL, a TLS material the runtime
|
|
81
|
+
// refuses — is a connection that closed before it opened, reported on the
|
|
82
|
+
// one channel an owner is already listening to.
|
|
83
|
+
this.disposed = true;
|
|
84
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
85
|
+
queueMicrotask(() => this.guard(() => this.handlers.onClose?.(reason)));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.socket = socket;
|
|
89
|
+
this.wire(socket);
|
|
90
|
+
}
|
|
91
|
+
wire(socket) {
|
|
92
|
+
socket.on("open", () => {
|
|
93
|
+
if (this.disposed)
|
|
94
|
+
return;
|
|
95
|
+
this.opened = true;
|
|
96
|
+
this.authenticated = false;
|
|
97
|
+
this.startHeartbeat(socket);
|
|
98
|
+
this.guard(() => this.handlers.onOpen?.());
|
|
99
|
+
// The auth frame, first, before anything else this transport or its owner
|
|
100
|
+
// wants to say. See this module's header, trap 2.
|
|
101
|
+
if (this.bearer) {
|
|
102
|
+
this.write({ type: "auth", reqId: this.nextReqId("auth"), bearer: this.bearer });
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
this.guard(() => this.handlers.onWritable?.());
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
socket.on("message", (raw) => {
|
|
109
|
+
this.lastInboundAt = this.now();
|
|
110
|
+
this.onMessage(raw);
|
|
111
|
+
});
|
|
112
|
+
// Pong frames prove the peer is alive during an idle stretch. Only the Node
|
|
113
|
+
// `ws` transport surfaces them; a browser WebSocket answers pings in the
|
|
114
|
+
// engine and never tells us, which is why the heartbeat only arms where
|
|
115
|
+
// `ping` exists at all.
|
|
116
|
+
socket.on("pong", () => {
|
|
117
|
+
this.lastInboundAt = this.now();
|
|
118
|
+
});
|
|
119
|
+
socket.on("error", (err) => {
|
|
120
|
+
// The close handler drives everything; errors are ordinary during a Core
|
|
121
|
+
// restart. All that is kept is the reason, so the close report can say
|
|
122
|
+
// something better than "it closed" — a TLS rejection and an
|
|
123
|
+
// ECONNREFUSED both arrive here and then close silently.
|
|
124
|
+
this.lastError = err instanceof Error ? err.message : String(err);
|
|
125
|
+
});
|
|
126
|
+
socket.on("close", () => {
|
|
127
|
+
const wasDisposed = this.disposed;
|
|
128
|
+
this.opened = false;
|
|
129
|
+
this.authenticated = false;
|
|
130
|
+
this.stopHeartbeat();
|
|
131
|
+
// Every request written to this socket can never be answered now, so
|
|
132
|
+
// failing them here is the difference between a caller retrying at once
|
|
133
|
+
// and one waiting out its full timeout first.
|
|
134
|
+
this.rejectAll(CONNECTION_LOST_MESSAGE);
|
|
135
|
+
this.socket = null;
|
|
136
|
+
if (wasDisposed)
|
|
137
|
+
return;
|
|
138
|
+
this.disposed = true;
|
|
139
|
+
this.guard(() => this.handlers.onClose?.(this.lastError));
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/** True while this connection can carry a frame — open, and authenticated if it must be. */
|
|
143
|
+
get writable() {
|
|
144
|
+
return !this.disposed && this.opened && (this.bearer === null || this.authenticated);
|
|
145
|
+
}
|
|
146
|
+
/** True once the Core has accepted this connection's bearer. */
|
|
147
|
+
get isAuthenticated() {
|
|
148
|
+
return this.authenticated;
|
|
149
|
+
}
|
|
150
|
+
/** This connection's `ready` frame, or null before it lands. */
|
|
151
|
+
get readyFrame() {
|
|
152
|
+
return this.ready;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Send a request frame and resolve with the Core's answer — **frames both
|
|
156
|
+
* ways**, errors included, because a router forwarding on behalf of somebody
|
|
157
|
+
* else needs the frame rather than an exception (that is the Panel's shape,
|
|
158
|
+
* and `CoreClient` is what turns a failure frame into a rejection).
|
|
159
|
+
*
|
|
160
|
+
* The `reqId` on the frame passed in is ignored: this transport owns
|
|
161
|
+
* correlation on its own socket and returns the frame carrying the id it
|
|
162
|
+
* assigned. Rejects if the socket dies before the answer arrives; never times
|
|
163
|
+
* out on its own — a deadline is the caller's policy, and the caller above
|
|
164
|
+
* this one has one.
|
|
165
|
+
*/
|
|
166
|
+
request(frame) {
|
|
167
|
+
if (!this.writable)
|
|
168
|
+
return Promise.reject(new Error(CONNECTION_LOST_MESSAGE));
|
|
169
|
+
const reqId = this.nextReqId("r");
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
this.pending.set(reqId, { resolve, reject });
|
|
172
|
+
if (!this.write({ ...frame, reqId })) {
|
|
173
|
+
this.pending.delete(reqId);
|
|
174
|
+
reject(new Error(CONNECTION_LOST_MESSAGE));
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Send a frame and forget it. For the frames whose answer is a *stream* rather
|
|
180
|
+
* than a response — `subscribe`, whose reply is the replay tail and the
|
|
181
|
+
* `eventsReplayed` marker — and for the ones nothing waits on by design.
|
|
182
|
+
* Returns the assigned `reqId`, or null when the transport could not take it.
|
|
183
|
+
*/
|
|
184
|
+
send(frame, reqIdPrefix = "s") {
|
|
185
|
+
if (!this.writable)
|
|
186
|
+
return null;
|
|
187
|
+
const reqId = this.nextReqId(reqIdPrefix);
|
|
188
|
+
return this.write({ ...frame, reqId }) ? reqId : null;
|
|
189
|
+
}
|
|
190
|
+
/** Hang up. Idempotent; fires no `onClose`, because this is not the Core going away. */
|
|
191
|
+
close() {
|
|
192
|
+
if (this.disposed)
|
|
193
|
+
return;
|
|
194
|
+
this.disposed = true;
|
|
195
|
+
this.stopHeartbeat();
|
|
196
|
+
this.rejectAll("core-link client closed");
|
|
197
|
+
const socket = this.socket;
|
|
198
|
+
this.socket = null;
|
|
199
|
+
this.opened = false;
|
|
200
|
+
this.authenticated = false;
|
|
201
|
+
if (socket) {
|
|
202
|
+
try {
|
|
203
|
+
socket.close();
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
/* best effort — the socket is going away either way */
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
nextReqId(prefix) {
|
|
211
|
+
return `${prefix}${++this.reqSeq}`;
|
|
212
|
+
}
|
|
213
|
+
write(frame) {
|
|
214
|
+
const socket = this.socket;
|
|
215
|
+
if (!socket || !this.opened)
|
|
216
|
+
return false;
|
|
217
|
+
try {
|
|
218
|
+
socket.send(JSON.stringify(frame));
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// The close handler takes it from here — it is the one place that fails
|
|
223
|
+
// in-flight requests and tells the owner.
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
onMessage(raw) {
|
|
228
|
+
let msg;
|
|
229
|
+
try {
|
|
230
|
+
msg = JSON.parse(typeof raw === "string" ? raw : String(raw));
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (!msg || typeof msg.type !== "string")
|
|
236
|
+
return;
|
|
237
|
+
switch (msg.type) {
|
|
238
|
+
// ─── The three unsolicited streams ───
|
|
239
|
+
case "ready": {
|
|
240
|
+
// Kept as the frame rather than picked apart: the version gate and the
|
|
241
|
+
// `multiConnection` capability are both read off it upstairs, and a
|
|
242
|
+
// transport that pre-digested it would have to grow a field per
|
|
243
|
+
// capability the `ready` frame ever gains.
|
|
244
|
+
this.ready = {
|
|
245
|
+
type: "ready",
|
|
246
|
+
version: typeof msg.version === "string" ? msg.version : "",
|
|
247
|
+
...(msg.multiConnection !== undefined
|
|
248
|
+
? { multiConnection: msg.multiConnection }
|
|
249
|
+
: {}),
|
|
250
|
+
};
|
|
251
|
+
this.guard(() => this.handlers.onReady?.(this.ready));
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
case "data": {
|
|
255
|
+
const ptyId = typeof msg.ptyId === "string" ? msg.ptyId : "";
|
|
256
|
+
if (!ptyId)
|
|
257
|
+
return;
|
|
258
|
+
const frame = {
|
|
259
|
+
type: "data",
|
|
260
|
+
ptyId,
|
|
261
|
+
data: typeof msg.data === "string" ? msg.data : "",
|
|
262
|
+
seq: typeof msg.seq === "number" ? msg.seq : 0,
|
|
263
|
+
};
|
|
264
|
+
this.guard(() => this.handlers.onData?.(frame));
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
case "exit": {
|
|
268
|
+
const ptyId = typeof msg.ptyId === "string" ? msg.ptyId : "";
|
|
269
|
+
if (!ptyId)
|
|
270
|
+
return;
|
|
271
|
+
const frame = {
|
|
272
|
+
type: "exit",
|
|
273
|
+
ptyId,
|
|
274
|
+
exitCode: typeof msg.exitCode === "number" ? msg.exitCode : 0,
|
|
275
|
+
...(typeof msg.signal === "number" ? { signal: msg.signal } : {}),
|
|
276
|
+
};
|
|
277
|
+
this.guard(() => this.handlers.onExit?.(frame));
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// ─── The event log ───
|
|
281
|
+
case "event": {
|
|
282
|
+
const event = parseEvent(msg.event);
|
|
283
|
+
if (!event)
|
|
284
|
+
return;
|
|
285
|
+
this.guard(() => this.handlers.onEvent?.(event));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
case "eventsReplayed": {
|
|
289
|
+
const lastEventId = typeof msg.lastEventId === "number" ? msg.lastEventId : 0;
|
|
290
|
+
this.guard(() => this.handlers.onEventsReplayed?.(lastEventId));
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
case "subscribeAck":
|
|
294
|
+
// Informational — the event stream and its `eventsReplayed` marker
|
|
295
|
+
// follow, and they are what a subscriber is waiting for.
|
|
296
|
+
return;
|
|
297
|
+
// ─── Bearer auth ───
|
|
298
|
+
case "authOk": {
|
|
299
|
+
this.authenticated = true;
|
|
300
|
+
const frame = {
|
|
301
|
+
type: "authOk",
|
|
302
|
+
reqId: typeof msg.reqId === "string" ? msg.reqId : "",
|
|
303
|
+
coreId: typeof msg.coreId === "string" ? msg.coreId : "",
|
|
304
|
+
exp: typeof msg.exp === "number" ? msg.exp : 0,
|
|
305
|
+
};
|
|
306
|
+
// The owner's turn first: a fresh connection owes the Core a `reclaim`
|
|
307
|
+
// and a `subscribe`, and both must precede whatever requests were
|
|
308
|
+
// waiting for this socket, so the replay tail arrives before the
|
|
309
|
+
// answers to them.
|
|
310
|
+
this.guard(() => this.handlers.onAuthOk?.(frame));
|
|
311
|
+
this.guard(() => this.handlers.onWritable?.());
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
case "authError": {
|
|
315
|
+
const reason = msg.reason === "expired" || msg.reason === "bad-signature" || msg.reason === "malformed"
|
|
316
|
+
? msg.reason
|
|
317
|
+
: "malformed";
|
|
318
|
+
this.authenticated = false;
|
|
319
|
+
this.guard(() => this.handlers.onAuthError?.(reason));
|
|
320
|
+
// The Core closes the socket right after this frame; the close handler
|
|
321
|
+
// is what reports the connection's end. An expired bearer keeps failing
|
|
322
|
+
// until a reissued blob is pasted — that is the designed "reissuing is a
|
|
323
|
+
// machine-side operation" property (ADR 0003).
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
default:
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
// ─── Everything else is an answer, correlated by reqId ───
|
|
330
|
+
const reqId = typeof msg.reqId === "string" ? msg.reqId : null;
|
|
331
|
+
if (!reqId)
|
|
332
|
+
return;
|
|
333
|
+
const pending = this.pending.get(reqId);
|
|
334
|
+
if (!pending)
|
|
335
|
+
return;
|
|
336
|
+
this.pending.delete(reqId);
|
|
337
|
+
pending.resolve(msg);
|
|
338
|
+
}
|
|
339
|
+
rejectAll(reason) {
|
|
340
|
+
if (this.pending.size === 0)
|
|
341
|
+
return;
|
|
342
|
+
const err = new Error(reason);
|
|
343
|
+
const pending = [...this.pending.values()];
|
|
344
|
+
this.pending.clear();
|
|
345
|
+
for (const p of pending)
|
|
346
|
+
p.reject(err);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Arm the heartbeat for a freshly opened socket. Only a transport that can
|
|
350
|
+
* actually send a ping participates; a socket without one is left alone
|
|
351
|
+
* rather than being torn down for failing to answer pings nobody sent.
|
|
352
|
+
*/
|
|
353
|
+
startHeartbeat(socket) {
|
|
354
|
+
this.stopHeartbeat();
|
|
355
|
+
if (!this.heartbeatOpts || !socket.ping)
|
|
356
|
+
return;
|
|
357
|
+
const intervalMs = this.heartbeatOpts.intervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
358
|
+
const timeoutMs = this.heartbeatOpts.timeoutMs ?? DEFAULT_HEARTBEAT_TIMEOUT_MS;
|
|
359
|
+
this.lastInboundAt = this.now();
|
|
360
|
+
this.heartbeatTimer = setInterval(() => {
|
|
361
|
+
if (this.socket !== socket) {
|
|
362
|
+
this.stopHeartbeat();
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (this.now() - this.lastInboundAt > timeoutMs) {
|
|
366
|
+
// Half-open: the peer stopped answering. A graceful close would wait for
|
|
367
|
+
// a FIN that is never coming, so destroy the socket and let the close
|
|
368
|
+
// handler run the ordinary path.
|
|
369
|
+
this.stopHeartbeat();
|
|
370
|
+
try {
|
|
371
|
+
socket.terminate ? socket.terminate() : socket.close();
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
/* already gone — the close handler still fires */
|
|
375
|
+
}
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
socket.ping?.();
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
/* the close handler will take it from here */
|
|
383
|
+
}
|
|
384
|
+
}, intervalMs);
|
|
385
|
+
}
|
|
386
|
+
stopHeartbeat() {
|
|
387
|
+
if (this.heartbeatTimer) {
|
|
388
|
+
clearInterval(this.heartbeatTimer);
|
|
389
|
+
this.heartbeatTimer = null;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Run an owner's callback. A listener that throws must not take the
|
|
394
|
+
* connection with it — half the callbacks here run inside a socket event, and
|
|
395
|
+
* an exception escaping one of those is an unhandled rejection at best and a
|
|
396
|
+
* connection stuck half-established at worst.
|
|
397
|
+
*/
|
|
398
|
+
guard(fn) {
|
|
399
|
+
try {
|
|
400
|
+
fn();
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
/* a listener's failure is not this connection's failure */
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Read an `event` frame's payload into a {@link CoreLinkEvent}, or null.
|
|
409
|
+
*
|
|
410
|
+
* Field-by-field rather than a cast, because this is the one frame whose body
|
|
411
|
+
* is a nested object from another process's database: an `eventId` that is
|
|
412
|
+
* missing or zero would silently corrupt the cursor that decides what gets
|
|
413
|
+
* replayed after the next reconnect, so it is the one field whose absence
|
|
414
|
+
* rejects the frame outright.
|
|
415
|
+
*/
|
|
416
|
+
function parseEvent(raw) {
|
|
417
|
+
if (!raw || typeof raw !== "object")
|
|
418
|
+
return null;
|
|
419
|
+
const e = raw;
|
|
420
|
+
const eventId = typeof e.eventId === "number" ? e.eventId : NaN;
|
|
421
|
+
if (!Number.isFinite(eventId) || eventId <= 0)
|
|
422
|
+
return null;
|
|
423
|
+
return {
|
|
424
|
+
eventId,
|
|
425
|
+
ts: typeof e.ts === "number" ? e.ts : 0,
|
|
426
|
+
kind: typeof e.kind === "string" ? e.kind : "",
|
|
427
|
+
ptyId: typeof e.ptyId === "string" ? e.ptyId : null,
|
|
428
|
+
taskId: typeof e.taskId === "string" ? e.taskId : null,
|
|
429
|
+
payload: typeof e.payload === "string" ? e.payload : "{}",
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
//# sourceMappingURL=core-link-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-link-transport.js","sourceRoot":"","sources":["../src/core-link-transport.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,8EAA8E;AAC9E,kFAAkF;AAClF,2EAA2E;AAC3E,yEAAyE;AACzE,gFAAgF;AAChF,0EAA0E;AAC1E,2CAA2C;AAC3C,EAAE;AACF,gFAAgF;AAChF,6EAA6E;AAC7E,+EAA+E;AAC/E,+EAA+E;AAC/E,6BAA6B;AAC7B,EAAE;AACF,2EAA2E;AAC3E,gCAAgC;AAChC,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,8DAA8D;AAC9D,oEAAoE;AACpE,gFAAgF;AAChF,iFAAiF;AACjF,iFAAiF;AACjF,uBAAuB;AACvB,+EAA+E;AAC/E,+EAA+E;AAC/E,6EAA6E;AA0F7E;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAM,CAAC;AACpD,iFAAiF;AACjF,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AAEnD,iFAAiF;AACjF,MAAM,CAAC,MAAM,uBAAuB,GAAG,2BAA2B,CAAC;AAOnE;;;;;GAKG;AACH,MAAM,OAAO,iBAAiB;IACX,QAAQ,CAA4B;IACpC,MAAM,CAAgB;IACtB,aAAa,CAAkC;IACxD,MAAM,GAA0B,IAAI,CAAC;IACrC,MAAM,GAAG,CAAC,CAAC;IACF,OAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC9C,MAAM,GAAG,KAAK,CAAC;IACf,aAAa,GAAG,KAAK,CAAC;IACtB,QAAQ,GAAG,KAAK,CAAC;IACzB,+EAA+E;IACvE,SAAS,CAAqB;IAC9B,KAAK,GAA8B,IAAI,CAAC;IACxC,cAAc,GAA0C,IAAI,CAAC;IACrE,gFAAgF;IACxE,aAAa,GAAG,CAAC,CAAC;IAC1B,qEAAqE;IACpD,GAAG,GAAiB,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IAEtD,YAAY,IAA8B;QACxC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;QAClC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC;QAChF,IAAI,MAAsB,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,sEAAsE;YACtE,0EAA0E;YAC1E,gDAAgD;YAChD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAChE,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACxE,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpB,CAAC;IAEO,IAAI,CAAC,MAAsB;QACjC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YACrB,IAAI,IAAI,CAAC,QAAQ;gBAAE,OAAO;YAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;YAC3B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC3C,0EAA0E;YAC1E,kDAAkD;YAClD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACnF,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YACjD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;YAC3B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAChC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,4EAA4E;QAC5E,yEAAyE;QACzE,wEAAwE;QACxE,wBAAwB;QACxB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YACrB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACzB,yEAAyE;YACzE,uEAAuE;YACvE,6DAA6D;YAC7D,yDAAyD;YACzD,IAAI,CAAC,SAAS,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC;YAClC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACpB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;YAC3B,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,qEAAqE;YACrE,wEAAwE;YACxE,8CAA8C;YAC9C,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;YACxC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,WAAW;gBAAE,OAAO;YACxB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC;IAED,4FAA4F;IAC5F,IAAI,QAAQ;QACV,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,aAAa,CAAC,CAAC;IACvF,CAAC;IAED,gEAAgE;IAChE,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,gEAAgE;IAChE,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,KAA2B;QACjC,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;QAC9E,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,IAAI,OAAO,CAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC5D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAA0B,CAAC,EAAE,CAAC;gBAC7D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC3B,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,IAAI,CAAC,KAA2B,EAAE,WAAW,GAAG,GAAG;QACjD,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAA0B,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAChF,CAAC;IAED,wFAAwF;IACxF,KAAK;QACH,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;QAC3B,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC;gBACH,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;YAAC,MAAM,CAAC;gBACP,uDAAuD;YACzD,CAAC;QACH,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,MAAc;QAC9B,OAAO,GAAG,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACrC,CAAC;IAEO,KAAK,CAAC,KAA2B;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;YACnC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;YACxE,0CAA0C;YAC1C,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,GAAY;QAC5B,IAAI,GAA4B,CAAC;QACjC,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAA4B,CAAC;QAC3F,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO;QAEjD,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,wCAAwC;YACxC,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,uEAAuE;gBACvE,oEAAoE;gBACpE,gEAAgE;gBAChE,2CAA2C;gBAC3C,IAAI,CAAC,KAAK,GAAG;oBACX,IAAI,EAAE,OAAO;oBACb,OAAO,EAAE,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;oBAC3D,GAAG,CAAC,GAAG,CAAC,eAAe,KAAK,SAAS;wBACnC,CAAC,CAAC,EAAE,eAAe,EAAE,GAAG,CAAC,eAAwD,EAAE;wBACnF,CAAC,CAAC,EAAE,CAAC;iBACR,CAAC;gBACF,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,KAAM,CAAC,CAAC,CAAC;gBACvD,OAAO;YACT,CAAC;YACD,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7D,IAAI,CAAC,KAAK;oBAAE,OAAO;gBACnB,MAAM,KAAK,GAAsB;oBAC/B,IAAI,EAAE,MAAM;oBACZ,KAAK;oBACL,IAAI,EAAE,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;oBAClD,GAAG,EAAE,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;iBAC/C,CAAC;gBACF,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;gBAChD,OAAO;YACT,CAAC;YACD,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7D,IAAI,CAAC,KAAK;oBAAE,OAAO;gBACnB,MAAM,KAAK,GAAsB;oBAC/B,IAAI,EAAE,MAAM;oBACZ,KAAK;oBACL,QAAQ,EAAE,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;oBAC7D,GAAG,CAAC,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAClE,CAAC;gBACF,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;gBAChD,OAAO;YACT,CAAC;YAED,wBAAwB;YACxB,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACpC,IAAI,CAAC,KAAK;oBAAE,OAAO;gBACnB,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;gBACjD,OAAO;YACT,CAAC;YACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;gBACtB,MAAM,WAAW,GAAG,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9E,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;gBAChE,OAAO;YACT,CAAC;YACD,KAAK,cAAc;gBACjB,mEAAmE;gBACnE,yDAAyD;gBACzD,OAAO;YAET,sBAAsB;YACtB,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC1B,MAAM,KAAK,GAAwB;oBACjC,IAAI,EAAE,QAAQ;oBACd,KAAK,EAAE,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;oBACrD,MAAM,EAAE,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;oBACxD,GAAG,EAAE,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;iBAC/C,CAAC;gBACF,uEAAuE;gBACvE,kEAAkE;gBAClE,iEAAiE;gBACjE,mBAAmB;gBACnB,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;gBAClD,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;gBAC/C,OAAO;YACT,CAAC;YACD,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,MAAM,MAAM,GACV,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,eAAe,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW;oBACtF,CAAC,CAAC,GAAG,CAAC,MAAM;oBACZ,CAAC,CAAC,WAAW,CAAC;gBAClB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;gBACtD,uEAAuE;gBACvE,wEAAwE;gBACxE,yEAAyE;gBACzE,+CAA+C;gBAC/C,OAAO;YACT,CAAC;YACD;gBACE,MAAM;QACV,CAAC;QAED,4DAA4D;QAC5D,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/D,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3B,OAAO,CAAC,OAAO,CAAC,GAAuC,CAAC,CAAC;IAC3D,CAAC;IAEO,SAAS,CAAC,MAAc;QAC9B,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QACpC,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,OAAO;YAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACK,cAAc,CAAC,MAAsB;QAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,MAAM,CAAC,IAAI;YAAE,OAAO;QAChD,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,UAAU,IAAI,6BAA6B,CAAC;QAClF,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,IAAI,4BAA4B,CAAC;QAC/E,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE;YACrC,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC3B,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrB,OAAO;YACT,CAAC;YACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,GAAG,SAAS,EAAE,CAAC;gBAChD,yEAAyE;gBACzE,sEAAsE;gBACtE,iCAAiC;gBACjC,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrB,IAAI,CAAC;oBACH,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBACzD,CAAC;gBAAC,MAAM,CAAC;oBACP,kDAAkD;gBACpD,CAAC;gBACD,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YAClB,CAAC;YAAC,MAAM,CAAC;gBACP,8CAA8C;YAChD,CAAC;QACH,CAAC,EAAE,UAAU,CAAC,CAAC;IACjB,CAAC;IAEO,aAAa;QACnB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,EAAc;QAC1B,IAAI,CAAC;YACH,EAAE,EAAE,CAAC;QACP,CAAC;QAAC,MAAM,CAAC;YACP,2DAA2D;QAC7D,CAAC;IACH,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,SAAS,UAAU,CAAC,GAAY;IAC9B,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAChE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3D,OAAO;QACL,OAAO;QACP,EAAE,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACvC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;QAC9C,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;QACnD,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;QACtD,OAAO,EAAE,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;KAC1D,CAAC;AACJ,CAAC"}
|