socketduct 0.1.25 → 0.1.26

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 (56) hide show
  1. package/AGENTS.md +2 -2
  2. package/changelog.d/20261010130509-bound-log-frame-compression-memory.md +1 -0
  3. package/docs/runbooks/jsonl-integrity-rollout.md +8 -6
  4. package/package.json +2 -2
  5. package/scripts/test.js +9 -14
  6. package/src/logging/compressed-log-writer.js +1 -1
  7. package/src/logging/zstd-frame-codec.js +15 -1
  8. package/test/e2e/gateway-process.test.js +93 -92
  9. package/test/e2e/logger-shutdown-process.test.js +40 -37
  10. package/test/e2e/reverse-client-recovery-process.test.js +107 -103
  11. package/test/e2e/reverse-gateway-process.test.js +328 -318
  12. package/test/integration/initial-target-recovery.test.js +333 -334
  13. package/test/integration/logging-cli.test.js +121 -119
  14. package/test/integration/logging-compressed-recovery.test.js +96 -95
  15. package/test/integration/multiplexed-compatibility.test.js +178 -177
  16. package/test/integration/multiplexed-corrections.test.js +360 -359
  17. package/test/integration/multiplexed-failures.test.js +253 -251
  18. package/test/integration/multiplexed-transport-set.test.js +334 -322
  19. package/test/integration/multiplexed-transport.test.js +135 -134
  20. package/test/integration/reverse-gateway-stream-id-reset.test.js +90 -89
  21. package/test/integration/reverse-gateway.test.js +2309 -2218
  22. package/test/integration/reverse-resume-continuity.test.js +219 -218
  23. package/test/integration/reverse-route-class.test.js +358 -341
  24. package/test/integration/socketduct-http-agent.test.js +314 -312
  25. package/test/integration/socketduct-https-agent.test.js +516 -485
  26. package/test/integration/stream-compression.test.js +862 -824
  27. package/test/integration/tcp-forwarding.test.js +2550 -2447
  28. package/test/integration/tls-forwarding.test.js +593 -592
  29. package/test/unit/auth/auth.test.js +43 -44
  30. package/test/unit/auth/static-token-store.test.js +9 -8
  31. package/test/unit/client/connect.test.js +41 -40
  32. package/test/unit/client/deferred-virtual-socket.test.js +95 -94
  33. package/test/unit/client/multiplexed-spool-budget.test.js +155 -154
  34. package/test/unit/client/multiplexed-transport-set.test.js +830 -748
  35. package/test/unit/client/socketduct-socket.test.js +768 -771
  36. package/test/unit/client/virtual-socket.test.js +198 -168
  37. package/test/unit/config/docker-gateway-files.test.js +224 -224
  38. package/test/unit/config/environment.test.js +472 -492
  39. package/test/unit/index.test.js +53 -52
  40. package/test/unit/logging/compressed-log-worker.test.js +23 -22
  41. package/test/unit/logging/compressed-log-writer.test.js +155 -154
  42. package/test/unit/logging/log-retention.test.js +52 -47
  43. package/test/unit/logging/log-segment-index.test.js +84 -81
  44. package/test/unit/logging/zstd-frame-codec.test.js +28 -19
  45. package/test/unit/protocol/compression.test.js +254 -298
  46. package/test/unit/protocol/frame.test.js +280 -276
  47. package/test/unit/protocol/v3-compression.test.js +21 -23
  48. package/test/unit/protocol/v3-frame.test.js +42 -41
  49. package/test/unit/release-configuration.test.js +15 -12
  50. package/test/unit/reverse/disconnect-attribution.test.js +65 -64
  51. package/test/unit/reverse-terminal-outcomes.test.js +53 -50
  52. package/test/unit/server/control-handler.test.js +232 -231
  53. package/test/unit/server/primary.test.js +51 -49
  54. package/test/unit/server/relay-runtime.test.js +280 -294
  55. package/test/unit/sessions/session.test.js +48 -62
  56. package/test/unit/spool/spool-file.test.js +401 -385
@@ -1,6 +1,5 @@
1
1
  /* eslint-disable jsdoc/escape-inline-tags, jsdoc/require-param-type, jsdoc/require-returns */
2
- import assert from "node:assert/strict"
3
- import test from "node:test"
2
+ import {describe, expect, it} from "@velocious/testing"
4
3
 
5
4
  import {VirtualSocket} from "../../../src/client/virtual-socket.js"
6
5
 
@@ -27,7 +26,7 @@ function fakeConnection() {
27
26
  _resumeReadable: () => { calls.resumed += 1 },
28
27
  _deliverReadable: (/** @type {number} */ _id, /** @type {number} */ bytes) => { calls.delivered += bytes },
29
28
  _acknowledgeReadable: (/** @type {number} */ _id, /** @type {number} */ bytes) => {
30
- assert(bytes <= calls.delivered - calls.acknowledged)
29
+ if (!(bytes <= calls.delivered - calls.acknowledged)) throw new Error("ACK exceeds delivered bytes")
31
30
  calls.acknowledged += bytes
32
31
  return Promise.resolve(calls.acknowledged)
33
32
  },
@@ -42,179 +41,210 @@ function fakeConnection() {
42
41
  }
43
42
  }
44
43
 
45
- test("VirtualSocket exposes normal connect metadata and compatibility methods", async () => {
46
- const fake = fakeConnection()
47
- const socket = new VirtualSocket(fake.connection, 1, {host: "target.internal", port: 443})
48
- /** @type {string[]} */
49
- const events = []
50
- socket.on("connect", () => events.push("connect"))
51
- socket.on("ready", () => events.push("ready"))
52
- socket._markConnected()
53
-
54
- assert.deepEqual(events, ["connect", "ready"])
55
- assert.equal(socket.connecting, false)
56
- assert.equal(socket.pending, false)
57
- assert.equal(socket.remoteAddress, "target.internal")
58
- assert.equal(socket.remotePort, 443)
59
- assert.deepEqual(socket.address(), {address: "127.0.0.1", family: "IPv4", port: 43210})
60
- assert.equal(socket.setNoDelay(), socket)
61
- assert.equal(socket.setKeepAlive(), socket)
62
- assert.equal(socket.ref(), socket)
63
- assert.equal(socket.unref(), socket)
64
- assert.equal(fake.calls.refs, 1)
65
- assert.equal(fake.calls.unrefs, 1)
66
- socket.destroy()
67
- await new Promise((resolve) => socket.once("close", resolve))
68
- })
69
-
70
- test("VirtualSocket write backpressure emits drain and end emits finish", async () => {
71
- const fake = fakeConnection()
72
- const socket = new VirtualSocket(fake.connection, 3, {
73
- host: "127.0.0.1",
74
- port: 80,
75
- writableHighWaterMark: 1,
76
- allowHalfOpen: true
44
+ describe("VirtualSocket", () => {
45
+ it("VirtualSocket exposes normal connect metadata and compatibility methods", async () => {
46
+ const fake = fakeConnection()
47
+ const socket = new VirtualSocket(fake.connection, 1, {host: "target.internal", port: 443})
48
+ /** @type {string[]} */
49
+ const events = []
50
+ socket.on("connect", () => events.push("connect"))
51
+ socket.on("ready", () => events.push("ready"))
52
+ socket._markConnected()
53
+
54
+ expect(events).toEqual(["connect", "ready"])
55
+ expect(socket.connecting).toBe(false)
56
+ expect(socket.pending).toBe(false)
57
+ expect(socket.remoteAddress).toBe("target.internal")
58
+ expect(socket.remotePort).toBe(443)
59
+ expect(socket.address()).toEqual({address: "127.0.0.1", family: "IPv4", port: 43210})
60
+ expect(socket.setNoDelay()).toBe(socket)
61
+ expect(socket.setKeepAlive()).toBe(socket)
62
+ expect(socket.ref()).toBe(socket)
63
+ expect(socket.unref()).toBe(socket)
64
+ expect(fake.calls.refs).toBe(1)
65
+ expect(fake.calls.unrefs).toBe(1)
66
+ socket.destroy()
67
+ await new Promise((resolve) => socket.once("close", resolve))
77
68
  })
78
- socket._markConnected()
79
- const drained = new Promise((resolve) => socket.once("drain", resolve))
80
- assert.equal(socket.write("payload"), false)
81
- await drained
82
- assert.equal(socket.bytesWritten, 7)
83
- const finished = new Promise((resolve) => socket.once("finish", resolve))
84
- socket.end()
85
- await finished
86
- assert.equal(fake.calls.ended, 1)
87
- socket.destroy()
88
- })
89
69
 
90
- test("VirtualSocket pause/resume, timeout, data and half-close remain stream-local", async () => {
91
- const fake = fakeConnection()
92
- const socket = new VirtualSocket(fake.connection, 5, {host: "127.0.0.1", port: 80, allowHalfOpen: true})
93
- socket._markConnected()
94
- socket.pause()
95
- const timeout = new Promise((resolve) => socket.setTimeout(10, () => resolve(undefined)))
96
- await timeout
97
- socket.setTimeout(0)
98
- const data = new Promise((resolve) => socket.once("data", (chunk) => resolve(chunk.toString())))
99
- socket._receive(Buffer.from("hello"))
100
- socket.resume()
101
- assert.equal(await data, "hello")
102
- assert.equal(socket.bytesRead, 5)
103
- const ended = new Promise((resolve) => socket.once("end", resolve))
104
- socket._receiveEnd()
105
- await ended
106
- assert(fake.calls.resumed > 0)
107
- socket.destroy()
108
- })
70
+ it("VirtualSocket write backpressure emits drain and end emits finish", async () => {
71
+ const fake = fakeConnection()
72
+ const socket = new VirtualSocket(fake.connection, 3, {
73
+ host: "127.0.0.1",
74
+ port: 80,
75
+ writableHighWaterMark: 1,
76
+ allowHalfOpen: true
77
+ })
78
+ socket._markConnected()
79
+ const drained = new Promise((resolve) => socket.once("drain", resolve))
80
+ expect(socket.write("payload")).toBe(false)
81
+ await drained
82
+ expect(socket.bytesWritten).toBe(7)
83
+ const finished = new Promise((resolve) => socket.once("finish", resolve))
84
+ socket.end()
85
+ await finished
86
+ expect(fake.calls.ended).toBe(1)
87
+ socket.destroy()
88
+ })
109
89
 
110
- test("VirtualSocket exposes opt-in manual receive ACK and durable write contracts", async () => {
111
- const fake = fakeConnection()
112
- const socket = new VirtualSocket(fake.connection, 7, {
113
- host: "127.0.0.1",
114
- port: 80,
115
- manualReceiveAck: true
90
+ it("VirtualSocket pause/resume, timeout, data and half-close remain stream-local", async () => {
91
+ const fake = fakeConnection()
92
+ const socket = new VirtualSocket(fake.connection, 5, {host: "127.0.0.1", port: 80, allowHalfOpen: true})
93
+ socket._markConnected()
94
+ socket.pause()
95
+ const timeout = new Promise((resolve) => socket.setTimeout(10, () => resolve(undefined)))
96
+ await timeout
97
+ socket.setTimeout(0)
98
+ const data = new Promise((resolve) => socket.once("data", (chunk) => resolve(chunk.toString())))
99
+ socket._receive(Buffer.from("hello"))
100
+ socket.resume()
101
+ expect(await data).toBe("hello")
102
+ expect(socket.bytesRead).toBe(5)
103
+ const ended = new Promise((resolve) => socket.once("end", resolve))
104
+ socket._receiveEnd()
105
+ await ended
106
+ expect(fake.calls.resumed > 0).toBeTruthy()
107
+ socket.destroy()
116
108
  })
117
- socket._markConnected()
118
109
 
119
- const acknowledged = new Promise((resolve, reject) => {
120
- socket.once("data", (chunk) => {
121
- socket.acknowledgeReceive(chunk.length).then(resolve, reject)
110
+ it("VirtualSocket exposes opt-in manual receive ACK and durable write contracts", async () => {
111
+ const fake = fakeConnection()
112
+ const socket = new VirtualSocket(fake.connection, 7, {
113
+ host: "127.0.0.1",
114
+ port: 80,
115
+ manualReceiveAck: true
116
+ })
117
+ socket._markConnected()
118
+
119
+ const acknowledged = new Promise((resolve, reject) => {
120
+ socket.once("data", (chunk) => {
121
+ socket.acknowledgeReceive(chunk.length).then(resolve, reject)
122
+ })
122
123
  })
124
+ socket._receive(Buffer.from("response"))
125
+ expect(await acknowledged).toBe(8)
126
+ expect(fake.calls.delivered).toBe(8)
127
+ expect(fake.calls.acknowledged).toBe(8)
128
+
129
+ await socket.writeDurable(new Uint8Array(Buffer.from("request")))
130
+ expect(fake.calls.durableWrites).toHaveLength(1)
131
+ const durableWrite = fake.calls.durableWrites[0]
132
+ expect(durableWrite).toBeDefined()
133
+ expect(durableWrite?.equals(Buffer.from("request"))).toBe(true)
134
+ expect(socket.bytesWritten).toBe(7)
135
+ // predicate-based check: thrown error must carry SOCKETDUCT_INVALID_RECEIVE_ACK
136
+ let error
137
+ try {
138
+ socket.acknowledgeReceive(1)
139
+ } catch (caught) {
140
+ error = caught
141
+ }
142
+ expect(error).toBeDefined()
143
+ expect(/** @type {{code?: string}} */ (error).code).toBe("SOCKETDUCT_INVALID_RECEIVE_ACK")
144
+ const closed = new Promise((resolve) => socket.once("close", resolve))
145
+ socket.destroy()
146
+ await closed
147
+ // predicate-based rejection: code ERR_STREAM_DESTROYED
148
+ let rejection
149
+ try {
150
+ await socket.acknowledgeReceive(1)
151
+ } catch (caught) {
152
+ rejection = caught
153
+ }
154
+ expect(rejection).toBeDefined()
155
+ expect(/** @type {{code?: string}} */ (rejection).code).toBe("ERR_STREAM_DESTROYED")
123
156
  })
124
- socket._receive(Buffer.from("response"))
125
- assert.equal(await acknowledged, 8)
126
- assert.equal(fake.calls.delivered, 8)
127
- assert.equal(fake.calls.acknowledged, 8)
128
-
129
- await socket.writeDurable(new Uint8Array(Buffer.from("request")))
130
- assert.deepEqual(fake.calls.durableWrites, [Buffer.from("request")])
131
- assert.equal(socket.bytesWritten, 7)
132
- assert.throws(
133
- () => socket.acknowledgeReceive(1),
134
- (error) => /** @type {{code?: string}} */ (error).code === "SOCKETDUCT_INVALID_RECEIVE_ACK"
135
- )
136
- const closed = new Promise((resolve) => socket.once("close", resolve))
137
- socket.destroy()
138
- await closed
139
- await assert.rejects(
140
- socket.acknowledgeReceive(1),
141
- (error) => /** @type {{code?: string}} */ (error).code === "ERR_STREAM_DESTROYED"
142
- )
143
- })
144
157
 
145
- test("VirtualSocket orders durable writes behind queued ordinary writes", async () => {
146
- const fake = fakeConnection()
147
- /** @type {string[]} */
148
- const delivered = []
149
- /** @type {(() => void) | undefined} */
150
- let releaseFirst
151
- const connection = /** @type {{_writeStream: (id: number, data: Buffer, callback: (error?: Error | null) => void) => void, _writeDurableStream: (id: number, data: Buffer, callback: (error?: Error | null) => void) => void}} */ (/** @type {unknown} */ (fake.connection))
152
- connection._writeStream = (_id, data, callback) => {
153
- delivered.push(data.toString())
154
- if (data.toString() === "A") releaseFirst = callback
155
- else callback()
156
- }
157
- connection._writeDurableStream = (_id, data, callback) => {
158
- delivered.push(data.toString())
159
- callback()
160
- }
161
- const socket = new VirtualSocket(fake.connection, 9, {host: "127.0.0.1", port: 80})
162
-
163
- const first = new Promise((resolve, reject) => socket.write("A", (error) => error ? reject(error) : resolve(undefined)))
164
- const second = new Promise((resolve, reject) => socket.write("B", (error) => error ? reject(error) : resolve(undefined)))
165
- const durable = socket.writeDurable("C")
166
- assert(releaseFirst !== undefined)
167
- releaseFirst()
168
- await Promise.all([first, second, durable])
169
-
170
- assert.deepEqual(delivered, ["A", "B", "C"])
171
- assert.equal(socket.bytesWritten, 3)
172
- socket.destroy()
173
- })
158
+ it("VirtualSocket orders durable writes behind queued ordinary writes", async () => {
159
+ const fake = fakeConnection()
160
+ /** @type {string[]} */
161
+ const delivered = []
162
+ /** @type {(() => void) | undefined} */
163
+ let releaseFirst
164
+ const connection = /** @type {{_writeStream: (id: number, data: Buffer, callback: (error?: Error | null) => void) => void, _writeDurableStream: (id: number, data: Buffer, callback: (error?: Error | null) => void) => void}} */ (/** @type {unknown} */ (fake.connection))
165
+ connection._writeStream = (_id, data, callback) => {
166
+ delivered.push(data.toString())
167
+ if (data.toString() === "A") releaseFirst = callback
168
+ else callback()
169
+ }
170
+ connection._writeDurableStream = (_id, data, callback) => {
171
+ delivered.push(data.toString())
172
+ callback()
173
+ }
174
+ const socket = new VirtualSocket(fake.connection, 9, {host: "127.0.0.1", port: 80})
175
+
176
+ const first = new Promise((resolve, reject) => socket.write("A", (error) => error ? reject(error) : resolve(undefined)))
177
+ const second = new Promise((resolve, reject) => socket.write("B", (error) => error ? reject(error) : resolve(undefined)))
178
+ const durable = socket.writeDurable("C")
179
+ if (releaseFirst === undefined) throw new Error("expected first write callback to be captured")
180
+ releaseFirst()
181
+ await Promise.all([first, second, durable])
182
+
183
+ expect(delivered).toEqual(["A", "B", "C"])
184
+ expect(socket.bytesWritten).toBe(3)
185
+ socket.destroy()
186
+ })
174
187
 
175
- test("VirtualSocket rejects durable writes after end while ordinary writes remain queued", async () => {
176
- const fake = fakeConnection()
177
- /** @type {string[]} */
178
- const delivered = []
179
- /** @type {(() => void) | undefined} */
180
- let releaseFirst
181
- const connection = /** @type {{_writeStream: (id: number, data: Buffer, callback: (error?: Error | null) => void) => void}} */ (/** @type {unknown} */ (fake.connection))
182
- connection._writeStream = (_id, data, callback) => {
183
- delivered.push(data.toString())
184
- if (data.toString() === "A") releaseFirst = callback
185
- else callback()
186
- }
187
- const socket = new VirtualSocket(fake.connection, 11, {host: "127.0.0.1", port: 80})
188
-
189
- const first = new Promise((resolve, reject) => socket.write("A", (error) => error ? reject(error) : resolve(undefined)))
190
- const second = new Promise((resolve, reject) => socket.write("B", (error) => error ? reject(error) : resolve(undefined)))
191
- const finished = new Promise((resolve) => socket.once("finish", resolve))
192
- socket.end()
193
- const rejected = assert.rejects(
194
- socket.writeDurable("late"),
195
- (error) => /** @type {{code?: string}} */ (error).code === "ERR_STREAM_WRITE_AFTER_END"
196
- )
197
-
198
- assert(releaseFirst !== undefined)
199
- releaseFirst()
200
- await Promise.all([first, second, finished, rejected])
201
- assert.deepEqual(delivered, ["A", "B"])
202
- assert.deepEqual(fake.calls.durableWrites, [])
203
- socket.destroy()
204
- })
188
+ it("VirtualSocket rejects durable writes after end while ordinary writes remain queued", async () => {
189
+ const fake = fakeConnection()
190
+ /** @type {string[]} */
191
+ const delivered = []
192
+ /** @type {(() => void) | undefined} */
193
+ let releaseFirst
194
+ const connection = /** @type {{_writeStream: (id: number, data: Buffer, callback: (error?: Error | null) => void) => void}} */ (/** @type {unknown} */ (fake.connection))
195
+ connection._writeStream = (_id, data, callback) => {
196
+ delivered.push(data.toString())
197
+ if (data.toString() === "A") releaseFirst = callback
198
+ else callback()
199
+ }
200
+ const socket = new VirtualSocket(fake.connection, 11, {host: "127.0.0.1", port: 80})
201
+
202
+ const first = new Promise((resolve, reject) => socket.write("A", (error) => error ? reject(error) : resolve(undefined)))
203
+ const second = new Promise((resolve, reject) => socket.write("B", (error) => error ? reject(error) : resolve(undefined)))
204
+ const finished = new Promise((resolve) => socket.once("finish", resolve))
205
+ socket.end()
206
+ // predicate-based rejection: code ERR_STREAM_WRITE_AFTER_END
207
+ const rejected = (async () => {
208
+ try {
209
+ await socket.writeDurable("late")
210
+ throw new Error("expected writeDurable to reject")
211
+ } catch (caught) {
212
+ const code = /** @type {{code?: string}} */ (caught).code
213
+ if (code !== "ERR_STREAM_WRITE_AFTER_END") throw caught
214
+ }
215
+ })()
216
+
217
+ if (releaseFirst === undefined) throw new Error("expected first write callback to be captured")
218
+ releaseFirst()
219
+ await Promise.all([first, second, finished, rejected])
220
+ expect(delivered).toEqual(["A", "B"])
221
+ expect(fake.calls.durableWrites).toEqual([])
222
+ socket.destroy()
223
+ })
205
224
 
206
- test("VirtualSocket keeps manual ACK opt-in and reports unsupported durable admission", async () => {
207
- const fake = fakeConnection()
208
- delete /** @type {{_writeDurableStream?: unknown}} */ (fake.connection)._writeDurableStream
209
- const socket = new VirtualSocket(fake.connection, 9, {host: "127.0.0.1", port: 80})
210
-
211
- assert.throws(
212
- () => socket.acknowledgeReceive(1),
213
- (error) => /** @type {{code?: string}} */ (error).code === "SOCKETDUCT_MANUAL_ACK_DISABLED"
214
- )
215
- await assert.rejects(
216
- socket.writeDurable("request"),
217
- (error) => /** @type {{code?: string}} */ (error).code === "SOCKETDUCT_DURABLE_WRITE_UNSUPPORTED"
218
- )
219
- socket.destroy()
225
+ it("VirtualSocket keeps manual ACK opt-in and reports unsupported durable admission", async () => {
226
+ const fake = fakeConnection()
227
+ delete /** @type {{_writeDurableStream?: unknown}} */ (fake.connection)._writeDurableStream
228
+ const socket = new VirtualSocket(fake.connection, 9, {host: "127.0.0.1", port: 80})
229
+
230
+ // predicate-based check: thrown error must carry SOCKETDUCT_MANUAL_ACK_DISABLED
231
+ let error
232
+ try {
233
+ socket.acknowledgeReceive(1)
234
+ } catch (caught) {
235
+ error = caught
236
+ }
237
+ expect(error).toBeDefined()
238
+ expect(/** @type {{code?: string}} */ (error).code).toBe("SOCKETDUCT_MANUAL_ACK_DISABLED")
239
+ // predicate-based rejection: code SOCKETDUCT_DURABLE_WRITE_UNSUPPORTED
240
+ let rejection
241
+ try {
242
+ await socket.writeDurable("request")
243
+ } catch (caught) {
244
+ rejection = caught
245
+ }
246
+ expect(rejection).toBeDefined()
247
+ expect(/** @type {{code?: string}} */ (rejection).code).toBe("SOCKETDUCT_DURABLE_WRITE_UNSUPPORTED")
248
+ socket.destroy()
249
+ })
220
250
  })