@inkbox/sdk 0.2.16 → 0.3.1
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/README.md +10 -1
- package/dist/_http.d.ts +24 -5
- package/dist/_http.d.ts.map +1 -1
- package/dist/_http.js +21 -11
- package/dist/_http.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/inkbox.d.ts +4 -0
- package/dist/inkbox.d.ts.map +1 -1
- package/dist/inkbox.js +5 -0
- package/dist/inkbox.js.map +1 -1
- package/dist/tunnels/_validation.d.ts +7 -0
- package/dist/tunnels/_validation.d.ts.map +1 -0
- package/dist/tunnels/_validation.js +27 -0
- package/dist/tunnels/_validation.js.map +1 -0
- package/dist/tunnels/client/_bridge.d.ts +35 -0
- package/dist/tunnels/client/_bridge.d.ts.map +1 -0
- package/dist/tunnels/client/_bridge.js +52 -0
- package/dist/tunnels/client/_bridge.js.map +1 -0
- package/dist/tunnels/client/_callable_streaming.d.ts +25 -0
- package/dist/tunnels/client/_callable_streaming.d.ts.map +1 -0
- package/dist/tunnels/client/_callable_streaming.js +158 -0
- package/dist/tunnels/client/_callable_streaming.js.map +1 -0
- package/dist/tunnels/client/_cert.d.ts +45 -0
- package/dist/tunnels/client/_cert.d.ts.map +1 -0
- package/dist/tunnels/client/_cert.js +193 -0
- package/dist/tunnels/client/_cert.js.map +1 -0
- package/dist/tunnels/client/_dispatch.d.ts +109 -0
- package/dist/tunnels/client/_dispatch.d.ts.map +1 -0
- package/dist/tunnels/client/_dispatch.js +314 -0
- package/dist/tunnels/client/_dispatch.js.map +1 -0
- package/dist/tunnels/client/_envelope.d.ts +55 -0
- package/dist/tunnels/client/_envelope.d.ts.map +1 -0
- package/dist/tunnels/client/_envelope.js +97 -0
- package/dist/tunnels/client/_envelope.js.map +1 -0
- package/dist/tunnels/client/_h1_server.d.ts +37 -0
- package/dist/tunnels/client/_h1_server.d.ts.map +1 -0
- package/dist/tunnels/client/_h1_server.js +433 -0
- package/dist/tunnels/client/_h1_server.js.map +1 -0
- package/dist/tunnels/client/_h2_transcode.d.ts +43 -0
- package/dist/tunnels/client/_h2_transcode.d.ts.map +1 -0
- package/dist/tunnels/client/_h2_transcode.js +488 -0
- package/dist/tunnels/client/_h2_transcode.js.map +1 -0
- package/dist/tunnels/client/_handler.d.ts +62 -0
- package/dist/tunnels/client/_handler.d.ts.map +1 -0
- package/dist/tunnels/client/_handler.js +121 -0
- package/dist/tunnels/client/_handler.js.map +1 -0
- package/dist/tunnels/client/_listener.d.ts +64 -0
- package/dist/tunnels/client/_listener.d.ts.map +1 -0
- package/dist/tunnels/client/_listener.js +113 -0
- package/dist/tunnels/client/_listener.js.map +1 -0
- package/dist/tunnels/client/_protocol.d.ts +67 -0
- package/dist/tunnels/client/_protocol.d.ts.map +1 -0
- package/dist/tunnels/client/_protocol.js +86 -0
- package/dist/tunnels/client/_protocol.js.map +1 -0
- package/dist/tunnels/client/_runtime.d.ts +143 -0
- package/dist/tunnels/client/_runtime.d.ts.map +1 -0
- package/dist/tunnels/client/_runtime.js +1679 -0
- package/dist/tunnels/client/_runtime.js.map +1 -0
- package/dist/tunnels/client/_state.d.ts +45 -0
- package/dist/tunnels/client/_state.d.ts.map +1 -0
- package/dist/tunnels/client/_state.js +165 -0
- package/dist/tunnels/client/_state.js.map +1 -0
- package/dist/tunnels/client/_tls.d.ts +50 -0
- package/dist/tunnels/client/_tls.d.ts.map +1 -0
- package/dist/tunnels/client/_tls.js +139 -0
- package/dist/tunnels/client/_tls.js.map +1 -0
- package/dist/tunnels/client/_upstream_tls.d.ts +25 -0
- package/dist/tunnels/client/_upstream_tls.d.ts.map +1 -0
- package/dist/tunnels/client/_upstream_tls.js +24 -0
- package/dist/tunnels/client/_upstream_tls.js.map +1 -0
- package/dist/tunnels/client/_url_forward.d.ts +92 -0
- package/dist/tunnels/client/_url_forward.d.ts.map +1 -0
- package/dist/tunnels/client/_url_forward.js +255 -0
- package/dist/tunnels/client/_url_forward.js.map +1 -0
- package/dist/tunnels/client/_validation.d.ts +27 -0
- package/dist/tunnels/client/_validation.d.ts.map +1 -0
- package/dist/tunnels/client/_validation.js +96 -0
- package/dist/tunnels/client/_validation.js.map +1 -0
- package/dist/tunnels/client/_ws.d.ts +149 -0
- package/dist/tunnels/client/_ws.d.ts.map +1 -0
- package/dist/tunnels/client/_ws.js +351 -0
- package/dist/tunnels/client/_ws.js.map +1 -0
- package/dist/tunnels/client/_ws_passthrough.d.ts +129 -0
- package/dist/tunnels/client/_ws_passthrough.d.ts.map +1 -0
- package/dist/tunnels/client/_ws_passthrough.js +432 -0
- package/dist/tunnels/client/_ws_passthrough.js.map +1 -0
- package/dist/tunnels/client/_ws_url_bridge.d.ts +71 -0
- package/dist/tunnels/client/_ws_url_bridge.d.ts.map +1 -0
- package/dist/tunnels/client/_ws_url_bridge.js +474 -0
- package/dist/tunnels/client/_ws_url_bridge.js.map +1 -0
- package/dist/tunnels/client/_ws_url_edge_bridge.d.ts +26 -0
- package/dist/tunnels/client/_ws_url_edge_bridge.d.ts.map +1 -0
- package/dist/tunnels/client/_ws_url_edge_bridge.js +256 -0
- package/dist/tunnels/client/_ws_url_edge_bridge.js.map +1 -0
- package/dist/tunnels/client/_wsframe.d.ts +142 -0
- package/dist/tunnels/client/_wsframe.d.ts.map +1 -0
- package/dist/tunnels/client/_wsframe.js +282 -0
- package/dist/tunnels/client/_wsframe.js.map +1 -0
- package/dist/tunnels/client/index.d.ts +101 -0
- package/dist/tunnels/client/index.d.ts.map +1 -0
- package/dist/tunnels/client/index.js +242 -0
- package/dist/tunnels/client/index.js.map +1 -0
- package/dist/tunnels/exceptions.d.ts +31 -0
- package/dist/tunnels/exceptions.d.ts.map +1 -0
- package/dist/tunnels/exceptions.js +68 -0
- package/dist/tunnels/exceptions.js.map +1 -0
- package/dist/tunnels/resources/tunnels.d.ts +73 -0
- package/dist/tunnels/resources/tunnels.d.ts.map +1 -0
- package/dist/tunnels/resources/tunnels.js +173 -0
- package/dist/tunnels/resources/tunnels.js.map +1 -0
- package/dist/tunnels/types.d.ts +99 -0
- package/dist/tunnels/types.d.ts.map +1 -0
- package/dist/tunnels/types.js +76 -0
- package/dist/tunnels/types.js.map +1 -0
- package/package.json +14 -5
- package/protocol/tunnel_protocol_constants.json +65 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* inkbox-tunnels/client/_ws_url_edge_bridge.ts
|
|
3
|
+
*
|
|
4
|
+
* Edge-mode URL WebSocket bridge.
|
|
5
|
+
*
|
|
6
|
+
* The third party's WS frames arrive over the bridge stream as
|
|
7
|
+
* length-prefixed JSON envelopes wrapped in outer WS BINARY frames
|
|
8
|
+
* (the standard inkbox bridge protocol). We translate each direction:
|
|
9
|
+
*
|
|
10
|
+
* * Bridge → upstream: outer WS frames → inner JSON envelopes →
|
|
11
|
+
* RFC 6455 frames (masked, h1 client-side) written to the upstream
|
|
12
|
+
* socket.
|
|
13
|
+
* * Upstream → bridge: RFC 6455 frames (server, unmasked) → JSON
|
|
14
|
+
* envelopes → outer WS BINARY frames (masked) sent on the bridge
|
|
15
|
+
* via ``WsBridgeIO.sendFrame``.
|
|
16
|
+
*
|
|
17
|
+
* PING / PONG control frames are answered locally and not propagated.
|
|
18
|
+
*/
|
|
19
|
+
import { WS_OPCODE_BINARY, WS_OPCODE_CLOSE, WS_OPCODE_PING, WS_OPCODE_PONG, WS_OPCODE_TEXT, WsEnvelopeDecoder, WsFrameDecoder, encodeWsEnvelope, encodeWsFrame, } from "./_wsframe.js";
|
|
20
|
+
import { decodeClientFrame } from "./_ws_passthrough.js";
|
|
21
|
+
export async function pumpWsUrlEdgeBridge(opts) {
|
|
22
|
+
const { upstream, bridge } = opts;
|
|
23
|
+
const socket = upstream.socket;
|
|
24
|
+
// Upstream → bridge.
|
|
25
|
+
// The inkbox bridge protocol carries complete WS messages inside
|
|
26
|
+
// ``websocket.send`` envelopes; it cannot represent fragmentation.
|
|
27
|
+
// RFC 6455 lets the upstream split a TEXT/BINARY message into a
|
|
28
|
+
// first frame (TEXT/BINARY, FIN=0) followed by CONTINUATION frames
|
|
29
|
+
// until FIN=1. Reassemble client-side so we emit one envelope per
|
|
30
|
+
// message even when upstream streams it as fragments.
|
|
31
|
+
const upstreamBuf = [];
|
|
32
|
+
if (upstream.leftover.length > 0)
|
|
33
|
+
upstreamBuf.push(upstream.leftover);
|
|
34
|
+
let upstreamClosed = false;
|
|
35
|
+
let bridgeClosed = false;
|
|
36
|
+
let messageOpcode = null;
|
|
37
|
+
let messageChunks = [];
|
|
38
|
+
// Wake the bridge.recv() iteration on abrupt upstream close. Without
|
|
39
|
+
// it, the bridge→upstream loop sits inside the iterator until the
|
|
40
|
+
// third party sends another frame (which an idle upstream peer
|
|
41
|
+
// crash leaves indefinitely).
|
|
42
|
+
let signalUpstreamClosed = () => { };
|
|
43
|
+
const upstreamClosedSignal = new Promise((resolve) => {
|
|
44
|
+
signalUpstreamClosed = resolve;
|
|
45
|
+
});
|
|
46
|
+
const drainUpstream = async () => {
|
|
47
|
+
while (!bridgeClosed) {
|
|
48
|
+
const decoded = decodeClientFrame(upstreamBuf, { requireMask: false });
|
|
49
|
+
if (decoded.kind === "need-more")
|
|
50
|
+
return;
|
|
51
|
+
if (decoded.kind === "rejected") {
|
|
52
|
+
upstreamClosed = true;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const { opcode, payload, fin } = decoded;
|
|
56
|
+
if (opcode === WS_OPCODE_PING) {
|
|
57
|
+
try {
|
|
58
|
+
socket.write(encodeWsFrame(WS_OPCODE_PONG, payload, { mask: true }));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (opcode === WS_OPCODE_PONG)
|
|
66
|
+
continue;
|
|
67
|
+
if (opcode === WS_OPCODE_CLOSE) {
|
|
68
|
+
const code = payload.length >= 2 ? payload.readUInt16BE(0) : 1000;
|
|
69
|
+
const env = encodeWsEnvelope({
|
|
70
|
+
type: "websocket.close",
|
|
71
|
+
code,
|
|
72
|
+
reason: "",
|
|
73
|
+
});
|
|
74
|
+
try {
|
|
75
|
+
await bridge.sendFrame(encodeWsFrame(WS_OPCODE_BINARY, env, { mask: true }));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
/* swallow */
|
|
79
|
+
}
|
|
80
|
+
upstreamClosed = true;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (opcode === WS_OPCODE_TEXT || opcode === WS_OPCODE_BINARY) {
|
|
84
|
+
// Start of a (possibly fragmented) message. RFC 6455 §5.4
|
|
85
|
+
// requires no two TEXT/BINARY without a FIN=1 between them.
|
|
86
|
+
if (messageOpcode !== null) {
|
|
87
|
+
// Defensive: upstream framed badly — drop and close.
|
|
88
|
+
upstreamClosed = true;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
messageOpcode = opcode;
|
|
92
|
+
messageChunks = [payload];
|
|
93
|
+
}
|
|
94
|
+
else if (opcode === 0x0) {
|
|
95
|
+
// CONTINUATION
|
|
96
|
+
if (messageOpcode === null) {
|
|
97
|
+
upstreamClosed = true;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
messageChunks.push(payload);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
// Unknown opcode — ignore safely.
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (fin && messageOpcode !== null) {
|
|
107
|
+
const full = Buffer.concat(messageChunks);
|
|
108
|
+
const startedOpcode = messageOpcode;
|
|
109
|
+
messageOpcode = null;
|
|
110
|
+
messageChunks = [];
|
|
111
|
+
if (startedOpcode === WS_OPCODE_TEXT) {
|
|
112
|
+
let text;
|
|
113
|
+
try {
|
|
114
|
+
text = full.toString("utf-8");
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
upstreamClosed = true;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const env = encodeWsEnvelope({ type: "websocket.send", text });
|
|
121
|
+
try {
|
|
122
|
+
await bridge.sendFrame(encodeWsFrame(WS_OPCODE_BINARY, env, { mask: true }));
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const env = encodeWsEnvelope({
|
|
130
|
+
type: "websocket.send",
|
|
131
|
+
bytes: full,
|
|
132
|
+
});
|
|
133
|
+
try {
|
|
134
|
+
await bridge.sendFrame(encodeWsFrame(WS_OPCODE_BINARY, env, { mask: true }));
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
let draining = false;
|
|
144
|
+
const triggerDrain = () => {
|
|
145
|
+
if (draining)
|
|
146
|
+
return;
|
|
147
|
+
draining = true;
|
|
148
|
+
drainUpstream()
|
|
149
|
+
.catch(() => undefined)
|
|
150
|
+
.finally(() => {
|
|
151
|
+
draining = false;
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
socket.on("data", (chunk) => {
|
|
155
|
+
upstreamBuf.push(chunk);
|
|
156
|
+
triggerDrain();
|
|
157
|
+
});
|
|
158
|
+
socket.once("close", () => {
|
|
159
|
+
upstreamClosed = true;
|
|
160
|
+
signalUpstreamClosed();
|
|
161
|
+
});
|
|
162
|
+
socket.once("error", () => {
|
|
163
|
+
upstreamClosed = true;
|
|
164
|
+
signalUpstreamClosed();
|
|
165
|
+
});
|
|
166
|
+
// The upstream may already be gone between openWsUpstream returning
|
|
167
|
+
// and pumpWsUrlEdgeBridge attaching listeners (especially for an
|
|
168
|
+
// upstream that destroys after writing 101). "close" is one-shot, so
|
|
169
|
+
// a listener attached after the fact never fires — check explicitly.
|
|
170
|
+
if (socket.destroyed) {
|
|
171
|
+
upstreamClosed = true;
|
|
172
|
+
signalUpstreamClosed();
|
|
173
|
+
}
|
|
174
|
+
// Kick off in case the upgrade already shipped trailing frame bytes.
|
|
175
|
+
triggerDrain();
|
|
176
|
+
// Bridge → upstream.
|
|
177
|
+
const frameDecoder = new WsFrameDecoder();
|
|
178
|
+
const envelopeDecoder = new WsEnvelopeDecoder();
|
|
179
|
+
const it = bridge.recv()[Symbol.asyncIterator]();
|
|
180
|
+
try {
|
|
181
|
+
while (!upstreamClosed && !bridgeClosed) {
|
|
182
|
+
const next = await Promise.race([
|
|
183
|
+
it.next(),
|
|
184
|
+
upstreamClosedSignal.then(() => ({ done: true, value: undefined })),
|
|
185
|
+
]);
|
|
186
|
+
if (next.done)
|
|
187
|
+
break;
|
|
188
|
+
const chunk = next.value;
|
|
189
|
+
for (const frame of frameDecoder.feed(chunk)) {
|
|
190
|
+
if (frame.opcode === WS_OPCODE_PING) {
|
|
191
|
+
await bridge.sendFrame(encodeWsFrame(WS_OPCODE_PONG, frame.payload, { mask: true }));
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (frame.opcode === WS_OPCODE_PONG)
|
|
195
|
+
continue;
|
|
196
|
+
if (frame.opcode === WS_OPCODE_CLOSE) {
|
|
197
|
+
bridgeClosed = true;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
if (frame.opcode === WS_OPCODE_BINARY ||
|
|
201
|
+
frame.opcode === WS_OPCODE_TEXT) {
|
|
202
|
+
for (const env of envelopeDecoder.feed(frame.payload)) {
|
|
203
|
+
if (env.type === "text") {
|
|
204
|
+
try {
|
|
205
|
+
socket.write(encodeWsFrame(WS_OPCODE_TEXT, Buffer.from(env.data, "utf-8"), { mask: true }));
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
bridgeClosed = true;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else if (env.type === "binary") {
|
|
213
|
+
try {
|
|
214
|
+
socket.write(encodeWsFrame(WS_OPCODE_BINARY, env.data, { mask: true }));
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
bridgeClosed = true;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else if (env.type === "close") {
|
|
222
|
+
const codeBuf = Buffer.alloc(2);
|
|
223
|
+
codeBuf.writeUInt16BE(env.code, 0);
|
|
224
|
+
try {
|
|
225
|
+
socket.write(encodeWsFrame(WS_OPCODE_CLOSE, codeBuf, { mask: true }));
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
/* swallow */
|
|
229
|
+
}
|
|
230
|
+
bridgeClosed = true;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (bridgeClosed)
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
/* bridge stream closed */
|
|
242
|
+
}
|
|
243
|
+
finally {
|
|
244
|
+
bridgeClosed = true;
|
|
245
|
+
// Release the recv iterator (esp. when we exited via the
|
|
246
|
+
// upstream-closed race) so the underlying h2 stream queue isn't
|
|
247
|
+
// left with a parked consumer.
|
|
248
|
+
try {
|
|
249
|
+
await it.return?.();
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
/* swallow */
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=_ws_url_edge_bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_ws_url_edge_bridge.js","sourceRoot":"","sources":["../../../src/tunnels/client/_ws_url_edge_bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAOzD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAwB;IAExB,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAE/B,qBAAqB;IACrB,iEAAiE;IACjE,mEAAmE;IACnE,gEAAgE;IAChE,mEAAmE;IACnE,kEAAkE;IAClE,sDAAsD;IACtD,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACtE,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,IAAI,aAAa,GAAa,EAAE,CAAC;IAEjC,qEAAqE;IACrE,kEAAkE;IAClE,+DAA+D;IAC/D,8BAA8B;IAC9B,IAAI,oBAAoB,GAAe,GAAG,EAAE,GAAE,CAAC,CAAC;IAChD,MAAM,oBAAoB,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACzD,oBAAoB,GAAG,OAAO,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,KAAK,IAAmB,EAAE;QAC9C,OAAO,CAAC,YAAY,EAAE,CAAC;YACrB,MAAM,OAAO,GAAG,iBAAiB,CAAC,WAAW,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;YACvE,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW;gBAAE,OAAO;YACzC,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAChC,cAAc,GAAG,IAAI,CAAC;gBACtB,OAAO;YACT,CAAC;YACD,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;YACzC,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;gBAC9B,IAAI,CAAC;oBACH,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,cAAc,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBACvE,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO;gBACT,CAAC;gBACD,SAAS;YACX,CAAC;YACD,IAAI,MAAM,KAAK,cAAc;gBAAE,SAAS;YACxC,IAAI,MAAM,KAAK,eAAe,EAAE,CAAC;gBAC/B,MAAM,IAAI,GACR,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBACvD,MAAM,GAAG,GAAG,gBAAgB,CAAC;oBAC3B,IAAI,EAAE,iBAAiB;oBACvB,IAAI;oBACJ,MAAM,EAAE,EAAE;iBACX,CAAC,CAAC;gBACH,IAAI,CAAC;oBACH,MAAM,MAAM,CAAC,SAAS,CACpB,aAAa,CAAC,gBAAgB,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CACrD,CAAC;gBACJ,CAAC;gBAAC,MAAM,CAAC;oBACP,aAAa;gBACf,CAAC;gBACD,cAAc,GAAG,IAAI,CAAC;gBACtB,OAAO;YACT,CAAC;YACD,IAAI,MAAM,KAAK,cAAc,IAAI,MAAM,KAAK,gBAAgB,EAAE,CAAC;gBAC7D,0DAA0D;gBAC1D,4DAA4D;gBAC5D,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;oBAC3B,qDAAqD;oBACrD,cAAc,GAAG,IAAI,CAAC;oBACtB,OAAO;gBACT,CAAC;gBACD,aAAa,GAAG,MAAM,CAAC;gBACvB,aAAa,GAAG,CAAC,OAAO,CAAC,CAAC;YAC5B,CAAC;iBAAM,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC1B,eAAe;gBACf,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;oBAC3B,cAAc,GAAG,IAAI,CAAC;oBACtB,OAAO;gBACT,CAAC;gBACD,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,kCAAkC;gBAClC,SAAS;YACX,CAAC;YACD,IAAI,GAAG,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;gBAClC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;gBAC1C,MAAM,aAAa,GAAG,aAAa,CAAC;gBACpC,aAAa,GAAG,IAAI,CAAC;gBACrB,aAAa,GAAG,EAAE,CAAC;gBACnB,IAAI,aAAa,KAAK,cAAc,EAAE,CAAC;oBACrC,IAAI,IAAY,CAAC;oBACjB,IAAI,CAAC;wBACH,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;oBAChC,CAAC;oBAAC,MAAM,CAAC;wBACP,cAAc,GAAG,IAAI,CAAC;wBACtB,OAAO;oBACT,CAAC;oBACD,MAAM,GAAG,GAAG,gBAAgB,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;oBAC/D,IAAI,CAAC;wBACH,MAAM,MAAM,CAAC,SAAS,CACpB,aAAa,CAAC,gBAAgB,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CACrD,CAAC;oBACJ,CAAC;oBAAC,MAAM,CAAC;wBACP,OAAO;oBACT,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,MAAM,GAAG,GAAG,gBAAgB,CAAC;wBAC3B,IAAI,EAAE,gBAAgB;wBACtB,KAAK,EAAE,IAAI;qBACZ,CAAC,CAAC;oBACH,IAAI,CAAC;wBACH,MAAM,MAAM,CAAC,SAAS,CACpB,aAAa,CAAC,gBAAgB,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CACrD,CAAC;oBACJ,CAAC;oBAAC,MAAM,CAAC;wBACP,OAAO;oBACT,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,YAAY,GAAG,GAAS,EAAE;QAC9B,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,aAAa,EAAE;aACZ,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;aACtB,OAAO,CAAC,GAAG,EAAE;YACZ,QAAQ,GAAG,KAAK,CAAC;QACnB,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;IAEF,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;QAClC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,YAAY,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;QACxB,cAAc,GAAG,IAAI,CAAC;QACtB,oBAAoB,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;QACxB,cAAc,GAAG,IAAI,CAAC;QACtB,oBAAoB,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IACH,oEAAoE;IACpE,iEAAiE;IACjE,qEAAqE;IACrE,qEAAqE;IACrE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,cAAc,GAAG,IAAI,CAAC;QACtB,oBAAoB,EAAE,CAAC;IACzB,CAAC;IACD,qEAAqE;IACrE,YAAY,EAAE,CAAC;IAEf,qBAAqB;IACrB,MAAM,YAAY,GAAG,IAAI,cAAc,EAAE,CAAC;IAC1C,MAAM,eAAe,GAAG,IAAI,iBAAiB,EAAE,CAAC;IAChD,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;IACjD,IAAI,CAAC;QACH,OAAO,CAAC,cAAc,IAAI,CAAC,YAAY,EAAE,CAAC;YACxC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;gBAC9B,EAAE,CAAC,IAAI,EAAE;gBACT,oBAAoB,CAAC,IAAI,CACvB,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAA2B,CACnE;aACF,CAAC,CAAC;YACH,IAAI,IAAI,CAAC,IAAI;gBAAE,MAAM;YACrB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YACzB,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7C,IAAI,KAAK,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;oBACpC,MAAM,MAAM,CAAC,SAAS,CACpB,aAAa,CAAC,cAAc,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAC7D,CAAC;oBACF,SAAS;gBACX,CAAC;gBACD,IAAI,KAAK,CAAC,MAAM,KAAK,cAAc;oBAAE,SAAS;gBAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,eAAe,EAAE,CAAC;oBACrC,YAAY,GAAG,IAAI,CAAC;oBACpB,MAAM;gBACR,CAAC;gBACD,IACE,KAAK,CAAC,MAAM,KAAK,gBAAgB;oBACjC,KAAK,CAAC,MAAM,KAAK,cAAc,EAC/B,CAAC;oBACD,KAAK,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;wBACtD,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;4BACxB,IAAI,CAAC;gCACH,MAAM,CAAC,KAAK,CACV,aAAa,CACX,cAAc,EACd,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,EAC9B,EAAE,IAAI,EAAE,IAAI,EAAE,CACf,CACF,CAAC;4BACJ,CAAC;4BAAC,MAAM,CAAC;gCACP,YAAY,GAAG,IAAI,CAAC;gCACpB,MAAM;4BACR,CAAC;wBACH,CAAC;6BAAM,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;4BACjC,IAAI,CAAC;gCACH,MAAM,CAAC,KAAK,CACV,aAAa,CACX,gBAAgB,EAChB,GAAG,CAAC,IAAI,EACR,EAAE,IAAI,EAAE,IAAI,EAAE,CACf,CACF,CAAC;4BACJ,CAAC;4BAAC,MAAM,CAAC;gCACP,YAAY,GAAG,IAAI,CAAC;gCACpB,MAAM;4BACR,CAAC;wBACH,CAAC;6BAAM,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;4BAChC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;4BAChC,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;4BACnC,IAAI,CAAC;gCACH,MAAM,CAAC,KAAK,CACV,aAAa,CAAC,eAAe,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CACxD,CAAC;4BACJ,CAAC;4BAAC,MAAM,CAAC;gCACP,aAAa;4BACf,CAAC;4BACD,YAAY,GAAG,IAAI,CAAC;4BACpB,MAAM;wBACR,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YACD,IAAI,YAAY;gBAAE,MAAM;QAC1B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,0BAA0B;IAC5B,CAAC;YAAS,CAAC;QACT,YAAY,GAAG,IAAI,CAAC;QACpB,yDAAyD;QACzD,gEAAgE;QAChE,+BAA+B;QAC/B,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,aAAa;QACf,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* inkbox-tunnels/client/_wsframe.ts
|
|
3
|
+
*
|
|
4
|
+
* RFC 6455 WebSocket frame codec, plus the length-prefixed JSON envelope
|
|
5
|
+
* format the WS-bridge stream carries.
|
|
6
|
+
*
|
|
7
|
+
* Pure / synchronous; no I/O. Used by both the WS upgrade bridge and
|
|
8
|
+
* the passthrough TCP bridge (which tunnels raw bytes inside WS BINARY
|
|
9
|
+
* frames on an extended-CONNECT stream).
|
|
10
|
+
*
|
|
11
|
+
* ## Statefulness — the decoder MUST accumulate across calls
|
|
12
|
+
*
|
|
13
|
+
* A single h2 DATA frame can carry zero, one, many, or partial WS
|
|
14
|
+
* frames; a single WS frame (with extended length) can span multiple
|
|
15
|
+
* DATA boundaries. Use {@link WsFrameDecoder.feed} which retains a
|
|
16
|
+
* carry buffer between calls. Do not implement
|
|
17
|
+
* one-frame-per-DATA-callback in callers.
|
|
18
|
+
*
|
|
19
|
+
* ## Partial-bytes-at-EOF policy (M3 T0 — matches Python)
|
|
20
|
+
*
|
|
21
|
+
* If the bridge stream ends ("end" or "reset" h2 event) while the carry
|
|
22
|
+
* buffer still contains a partial WS frame, the policy is:
|
|
23
|
+
* **drop the trailing bytes silently and close the WS session.**
|
|
24
|
+
* No RST_STREAM. No error surfaced to the user.
|
|
25
|
+
*
|
|
26
|
+
* Verified against Python `_runtime.py` (`_pump_ws`): on a stream-end /
|
|
27
|
+
* stream-reset event, `recv_done` is set and the loop exits, abandoning
|
|
28
|
+
* `wire_buf` and `env_buf` without any cleanup write. The TS port
|
|
29
|
+
* mirrors that exactly.
|
|
30
|
+
*/
|
|
31
|
+
export declare const WS_OPCODE_CONTINUATION = 0;
|
|
32
|
+
export declare const WS_OPCODE_TEXT = 1;
|
|
33
|
+
export declare const WS_OPCODE_BINARY = 2;
|
|
34
|
+
export declare const WS_OPCODE_CLOSE = 8;
|
|
35
|
+
export declare const WS_OPCODE_PING = 9;
|
|
36
|
+
export declare const WS_OPCODE_PONG = 10;
|
|
37
|
+
export interface WsFrame {
|
|
38
|
+
opcode: number;
|
|
39
|
+
payload: Buffer;
|
|
40
|
+
fin: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Stateful WS frame decoder. Hold one per bridge stream; call
|
|
44
|
+
* {@link feed} as h2 DATA chunks arrive.
|
|
45
|
+
*/
|
|
46
|
+
export declare class WsFrameDecoder {
|
|
47
|
+
private buf;
|
|
48
|
+
/**
|
|
49
|
+
* Feed a chunk of wire bytes; return any newly-decodable frames.
|
|
50
|
+
* Trailing partial bytes stay in the carry buffer for the next call.
|
|
51
|
+
*/
|
|
52
|
+
feed(chunk: Buffer): WsFrame[];
|
|
53
|
+
/** True iff there are partial bytes still in the carry buffer. */
|
|
54
|
+
hasPartial(): boolean;
|
|
55
|
+
/** Bytes currently buffered (test-only inspection). */
|
|
56
|
+
partialBytes(): number;
|
|
57
|
+
}
|
|
58
|
+
export interface EncodeOptions {
|
|
59
|
+
/** RFC 6455 requires client→server frames to be masked. Default: true. */
|
|
60
|
+
mask?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* RFC 6455 FIN bit. Default: true (single-frame message). Set false to
|
|
63
|
+
* produce a fragment that expects a continuation; needed by the URL
|
|
64
|
+
* passthrough bridge so multi-frame messages aren't silently coalesced.
|
|
65
|
+
*/
|
|
66
|
+
fin?: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Encode a single WS frame. ``mask=true`` is required for
|
|
70
|
+
* client→server traffic per RFC 6455.
|
|
71
|
+
*/
|
|
72
|
+
export declare function encodeWsFrame(opcode: number, payload: Buffer, options?: EncodeOptions): Buffer;
|
|
73
|
+
/**
|
|
74
|
+
* Outbound WS-envelope shape exchanged on the bridge stream.
|
|
75
|
+
*
|
|
76
|
+
* The wire shape is `length-prefixed (4 BE bytes) JSON`:
|
|
77
|
+
*
|
|
78
|
+
* - `{type: "text", data: <utf-8 string>}`
|
|
79
|
+
* - `{type: "binary", data: <base64 ascii>}`
|
|
80
|
+
* - `{type: "close", code: <int>, reason: <string>}`
|
|
81
|
+
*
|
|
82
|
+
* Binary payloads are base64-wrapped to match the server's bridge
|
|
83
|
+
* contract (server `b64encode`/`b64decode`).
|
|
84
|
+
*/
|
|
85
|
+
export type OutboundWsMsg = {
|
|
86
|
+
type: "websocket.send";
|
|
87
|
+
text: string;
|
|
88
|
+
} | {
|
|
89
|
+
type: "websocket.send";
|
|
90
|
+
bytes: Buffer;
|
|
91
|
+
} | {
|
|
92
|
+
type: "websocket.close";
|
|
93
|
+
code?: number;
|
|
94
|
+
reason?: string;
|
|
95
|
+
};
|
|
96
|
+
export declare function encodeWsEnvelope(msg: OutboundWsMsg): Buffer;
|
|
97
|
+
/**
|
|
98
|
+
* Inbound bridge envelope decoded from a string/binary WS frame
|
|
99
|
+
* payload.
|
|
100
|
+
*/
|
|
101
|
+
export type InboundWsEnvelope = {
|
|
102
|
+
type: "text";
|
|
103
|
+
data: string;
|
|
104
|
+
} | {
|
|
105
|
+
type: "binary";
|
|
106
|
+
data: Buffer;
|
|
107
|
+
} | {
|
|
108
|
+
type: "close";
|
|
109
|
+
code: number;
|
|
110
|
+
reason?: string;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Length-prefixed-JSON envelope decoder. Stateful — call repeatedly
|
|
114
|
+
* with concatenated WS-frame payloads, get back fully-formed envelopes
|
|
115
|
+
* as they emerge.
|
|
116
|
+
*
|
|
117
|
+
* Binary envelopes have their `data` field strictly base64-validated
|
|
118
|
+
* (per the server contract). Malformed base64 is logged and dropped —
|
|
119
|
+
* the empty result is what the runtime delivers, mirroring Python's
|
|
120
|
+
* `_ws.py` behavior at the validate=True boundary.
|
|
121
|
+
*/
|
|
122
|
+
export declare class WsEnvelopeDecoder {
|
|
123
|
+
private buf;
|
|
124
|
+
feed(chunk: Buffer): InboundWsEnvelope[];
|
|
125
|
+
hasPartial(): boolean;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Strict base64 decode: rejects non-base64 characters, requires
|
|
129
|
+
* padding. Mirrors Python `base64.b64decode(..., validate=True)`.
|
|
130
|
+
*
|
|
131
|
+
* Node's `Buffer.from(s, "base64")` is permissive (silently strips
|
|
132
|
+
* non-base64 chars and tolerates missing padding). We need the strict
|
|
133
|
+
* shape to match the server's outbound encoding exactly — otherwise a
|
|
134
|
+
* garbage `"@@@@"` decodes to an empty Buffer the user's app would
|
|
135
|
+
* mistake for a real binary message.
|
|
136
|
+
*/
|
|
137
|
+
declare function decodeStrictBase64(s: string): Buffer | null;
|
|
138
|
+
export declare const __testing: {
|
|
139
|
+
decodeStrictBase64: typeof decodeStrictBase64;
|
|
140
|
+
};
|
|
141
|
+
export {};
|
|
142
|
+
//# sourceMappingURL=_wsframe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_wsframe.d.ts","sourceRoot":"","sources":["../../../src/tunnels/client/_wsframe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,eAAO,MAAM,sBAAsB,IAAM,CAAC;AAC1C,eAAO,MAAM,cAAc,IAAM,CAAC;AAClC,eAAO,MAAM,gBAAgB,IAAM,CAAC;AACpC,eAAO,MAAM,eAAe,IAAM,CAAC;AACnC,eAAO,MAAM,cAAc,IAAM,CAAC;AAClC,eAAO,MAAM,cAAc,KAAM,CAAC;AAElC,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;CACd;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,GAAG,CAA2B;IAEtC;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE;IAsD9B,kEAAkE;IAClE,UAAU,IAAI,OAAO;IAIrB,uDAAuD;IACvD,YAAY,IAAI,MAAM;CAGvB;AAED,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,MAAM,CAoCR;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAwB3D;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD;;;;;;;;;GASG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,GAAG,CAA2B;IAEtC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,EAAE;IA4BxC,UAAU,IAAI,OAAO;CAGtB;AA2BD;;;;;;;;;GASG;AACH,iBAAS,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASpD;AAED,eAAO,MAAM,SAAS;;CAAyB,CAAC"}
|