@inkbox/sdk 0.2.15 → 0.3.0

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.
Files changed (135) hide show
  1. package/README.md +10 -1
  2. package/dist/_http.d.ts +24 -5
  3. package/dist/_http.d.ts.map +1 -1
  4. package/dist/_http.js +21 -11
  5. package/dist/_http.js.map +1 -1
  6. package/dist/agent_identity.d.ts +20 -0
  7. package/dist/agent_identity.d.ts.map +1 -1
  8. package/dist/agent_identity.js +17 -0
  9. package/dist/agent_identity.js.map +1 -1
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/inkbox.d.ts +4 -0
  15. package/dist/inkbox.d.ts.map +1 -1
  16. package/dist/inkbox.js +5 -0
  17. package/dist/inkbox.js.map +1 -1
  18. package/dist/phone/resources/calls.d.ts +8 -0
  19. package/dist/phone/resources/calls.d.ts.map +1 -1
  20. package/dist/phone/resources/calls.js +15 -1
  21. package/dist/phone/resources/calls.js.map +1 -1
  22. package/dist/phone/resources/texts.d.ts +26 -0
  23. package/dist/phone/resources/texts.d.ts.map +1 -1
  24. package/dist/phone/resources/texts.js +42 -2
  25. package/dist/phone/resources/texts.js.map +1 -1
  26. package/dist/phone/types.d.ts +18 -0
  27. package/dist/phone/types.d.ts.map +1 -1
  28. package/dist/phone/types.js +2 -0
  29. package/dist/phone/types.js.map +1 -1
  30. package/dist/tunnels/_validation.d.ts +7 -0
  31. package/dist/tunnels/_validation.d.ts.map +1 -0
  32. package/dist/tunnels/_validation.js +27 -0
  33. package/dist/tunnels/_validation.js.map +1 -0
  34. package/dist/tunnels/client/_bridge.d.ts +35 -0
  35. package/dist/tunnels/client/_bridge.d.ts.map +1 -0
  36. package/dist/tunnels/client/_bridge.js +52 -0
  37. package/dist/tunnels/client/_bridge.js.map +1 -0
  38. package/dist/tunnels/client/_callable_streaming.d.ts +25 -0
  39. package/dist/tunnels/client/_callable_streaming.d.ts.map +1 -0
  40. package/dist/tunnels/client/_callable_streaming.js +158 -0
  41. package/dist/tunnels/client/_callable_streaming.js.map +1 -0
  42. package/dist/tunnels/client/_cert.d.ts +45 -0
  43. package/dist/tunnels/client/_cert.d.ts.map +1 -0
  44. package/dist/tunnels/client/_cert.js +193 -0
  45. package/dist/tunnels/client/_cert.js.map +1 -0
  46. package/dist/tunnels/client/_dispatch.d.ts +109 -0
  47. package/dist/tunnels/client/_dispatch.d.ts.map +1 -0
  48. package/dist/tunnels/client/_dispatch.js +314 -0
  49. package/dist/tunnels/client/_dispatch.js.map +1 -0
  50. package/dist/tunnels/client/_envelope.d.ts +55 -0
  51. package/dist/tunnels/client/_envelope.d.ts.map +1 -0
  52. package/dist/tunnels/client/_envelope.js +97 -0
  53. package/dist/tunnels/client/_envelope.js.map +1 -0
  54. package/dist/tunnels/client/_h1_server.d.ts +37 -0
  55. package/dist/tunnels/client/_h1_server.d.ts.map +1 -0
  56. package/dist/tunnels/client/_h1_server.js +433 -0
  57. package/dist/tunnels/client/_h1_server.js.map +1 -0
  58. package/dist/tunnels/client/_h2_transcode.d.ts +43 -0
  59. package/dist/tunnels/client/_h2_transcode.d.ts.map +1 -0
  60. package/dist/tunnels/client/_h2_transcode.js +488 -0
  61. package/dist/tunnels/client/_h2_transcode.js.map +1 -0
  62. package/dist/tunnels/client/_handler.d.ts +62 -0
  63. package/dist/tunnels/client/_handler.d.ts.map +1 -0
  64. package/dist/tunnels/client/_handler.js +121 -0
  65. package/dist/tunnels/client/_handler.js.map +1 -0
  66. package/dist/tunnels/client/_listener.d.ts +64 -0
  67. package/dist/tunnels/client/_listener.d.ts.map +1 -0
  68. package/dist/tunnels/client/_listener.js +113 -0
  69. package/dist/tunnels/client/_listener.js.map +1 -0
  70. package/dist/tunnels/client/_protocol.d.ts +67 -0
  71. package/dist/tunnels/client/_protocol.d.ts.map +1 -0
  72. package/dist/tunnels/client/_protocol.js +86 -0
  73. package/dist/tunnels/client/_protocol.js.map +1 -0
  74. package/dist/tunnels/client/_runtime.d.ts +143 -0
  75. package/dist/tunnels/client/_runtime.d.ts.map +1 -0
  76. package/dist/tunnels/client/_runtime.js +1679 -0
  77. package/dist/tunnels/client/_runtime.js.map +1 -0
  78. package/dist/tunnels/client/_state.d.ts +45 -0
  79. package/dist/tunnels/client/_state.d.ts.map +1 -0
  80. package/dist/tunnels/client/_state.js +165 -0
  81. package/dist/tunnels/client/_state.js.map +1 -0
  82. package/dist/tunnels/client/_tls.d.ts +50 -0
  83. package/dist/tunnels/client/_tls.d.ts.map +1 -0
  84. package/dist/tunnels/client/_tls.js +139 -0
  85. package/dist/tunnels/client/_tls.js.map +1 -0
  86. package/dist/tunnels/client/_upstream_tls.d.ts +25 -0
  87. package/dist/tunnels/client/_upstream_tls.d.ts.map +1 -0
  88. package/dist/tunnels/client/_upstream_tls.js +24 -0
  89. package/dist/tunnels/client/_upstream_tls.js.map +1 -0
  90. package/dist/tunnels/client/_url_forward.d.ts +92 -0
  91. package/dist/tunnels/client/_url_forward.d.ts.map +1 -0
  92. package/dist/tunnels/client/_url_forward.js +255 -0
  93. package/dist/tunnels/client/_url_forward.js.map +1 -0
  94. package/dist/tunnels/client/_validation.d.ts +27 -0
  95. package/dist/tunnels/client/_validation.d.ts.map +1 -0
  96. package/dist/tunnels/client/_validation.js +96 -0
  97. package/dist/tunnels/client/_validation.js.map +1 -0
  98. package/dist/tunnels/client/_ws.d.ts +149 -0
  99. package/dist/tunnels/client/_ws.d.ts.map +1 -0
  100. package/dist/tunnels/client/_ws.js +351 -0
  101. package/dist/tunnels/client/_ws.js.map +1 -0
  102. package/dist/tunnels/client/_ws_passthrough.d.ts +129 -0
  103. package/dist/tunnels/client/_ws_passthrough.d.ts.map +1 -0
  104. package/dist/tunnels/client/_ws_passthrough.js +432 -0
  105. package/dist/tunnels/client/_ws_passthrough.js.map +1 -0
  106. package/dist/tunnels/client/_ws_url_bridge.d.ts +71 -0
  107. package/dist/tunnels/client/_ws_url_bridge.d.ts.map +1 -0
  108. package/dist/tunnels/client/_ws_url_bridge.js +474 -0
  109. package/dist/tunnels/client/_ws_url_bridge.js.map +1 -0
  110. package/dist/tunnels/client/_ws_url_edge_bridge.d.ts +26 -0
  111. package/dist/tunnels/client/_ws_url_edge_bridge.d.ts.map +1 -0
  112. package/dist/tunnels/client/_ws_url_edge_bridge.js +256 -0
  113. package/dist/tunnels/client/_ws_url_edge_bridge.js.map +1 -0
  114. package/dist/tunnels/client/_wsframe.d.ts +142 -0
  115. package/dist/tunnels/client/_wsframe.d.ts.map +1 -0
  116. package/dist/tunnels/client/_wsframe.js +282 -0
  117. package/dist/tunnels/client/_wsframe.js.map +1 -0
  118. package/dist/tunnels/client/index.d.ts +101 -0
  119. package/dist/tunnels/client/index.d.ts.map +1 -0
  120. package/dist/tunnels/client/index.js +242 -0
  121. package/dist/tunnels/client/index.js.map +1 -0
  122. package/dist/tunnels/exceptions.d.ts +31 -0
  123. package/dist/tunnels/exceptions.d.ts.map +1 -0
  124. package/dist/tunnels/exceptions.js +68 -0
  125. package/dist/tunnels/exceptions.js.map +1 -0
  126. package/dist/tunnels/resources/tunnels.d.ts +73 -0
  127. package/dist/tunnels/resources/tunnels.d.ts.map +1 -0
  128. package/dist/tunnels/resources/tunnels.js +173 -0
  129. package/dist/tunnels/resources/tunnels.js.map +1 -0
  130. package/dist/tunnels/types.d.ts +99 -0
  131. package/dist/tunnels/types.d.ts.map +1 -0
  132. package/dist/tunnels/types.js +76 -0
  133. package/dist/tunnels/types.js.map +1 -0
  134. package/package.json +14 -5
  135. 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":"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"}
@@ -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"}