@copilotkit/aimock 1.42.1 → 1.44.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +65 -0
- package/README.md +108 -16
- package/dist/agui-handler.d.ts.map +1 -1
- package/dist/agui-mock.cjs +1 -1
- package/dist/agui-mock.cjs.map +1 -1
- package/dist/agui-mock.d.cts.map +1 -1
- package/dist/agui-mock.d.ts.map +1 -1
- package/dist/agui-mock.js +1 -1
- package/dist/agui-mock.js.map +1 -1
- package/dist/agui-recorder.cjs +33 -11
- package/dist/agui-recorder.cjs.map +1 -1
- package/dist/agui-recorder.d.cts +10 -1
- package/dist/agui-recorder.d.cts.map +1 -1
- package/dist/agui-recorder.d.ts +10 -1
- package/dist/agui-recorder.d.ts.map +1 -1
- package/dist/agui-recorder.js +33 -11
- package/dist/agui-recorder.js.map +1 -1
- package/dist/agui-types.d.cts +1 -0
- package/dist/agui-types.d.cts.map +1 -1
- package/dist/agui-types.d.ts +1 -0
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/aimock-cli.cjs +38 -10
- package/dist/aimock-cli.cjs.map +1 -1
- package/dist/aimock-cli.d.cts +5 -0
- package/dist/aimock-cli.d.cts.map +1 -1
- package/dist/aimock-cli.d.ts +5 -0
- package/dist/aimock-cli.d.ts.map +1 -1
- package/dist/aimock-cli.js +38 -10
- package/dist/aimock-cli.js.map +1 -1
- package/dist/batches.cjs +458 -0
- package/dist/batches.cjs.map +1 -0
- package/dist/batches.d.cts +57 -0
- package/dist/batches.d.cts.map +1 -0
- package/dist/batches.d.ts +57 -0
- package/dist/batches.d.ts.map +1 -0
- package/dist/batches.js +453 -0
- package/dist/batches.js.map +1 -0
- package/dist/bedrock-converse.cjs +44 -12
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +46 -14
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +10 -8
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +12 -10
- package/dist/bedrock.js.map +1 -1
- package/dist/byteplus-video.cjs +3 -3
- package/dist/byteplus-video.cjs.map +1 -1
- package/dist/byteplus-video.js +4 -4
- package/dist/byteplus-video.js.map +1 -1
- package/dist/chaos.cjs +625 -72
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts +164 -7
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts +164 -7
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +616 -74
- package/dist/chaos.js.map +1 -1
- package/dist/cli.cjs +131 -30
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +132 -31
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +37 -9
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +39 -11
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.cjs +1 -1
- package/dist/config-loader.js +1 -1
- package/dist/constants.cjs +8 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +6 -2
- package/dist/constants.d.ts +6 -2
- package/dist/constants.js +8 -1
- package/dist/constants.js.map +1 -1
- package/dist/convert.cjs +1 -1
- package/dist/convert.cjs.map +1 -1
- package/dist/convert.js +1 -1
- package/dist/convert.js.map +1 -1
- package/dist/echo-text.cjs +65 -0
- package/dist/echo-text.cjs.map +1 -0
- package/dist/echo-text.d.cts +22 -0
- package/dist/echo-text.d.cts.map +1 -0
- package/dist/echo-text.d.ts +22 -0
- package/dist/echo-text.d.ts.map +1 -0
- package/dist/echo-text.js +65 -0
- package/dist/echo-text.js.map +1 -0
- package/dist/elevenlabs-audio.cjs +3 -3
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.js +4 -4
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/elevenlabs-voice.cjs +1053 -0
- package/dist/elevenlabs-voice.cjs.map +1 -0
- package/dist/elevenlabs-voice.d.cts +72 -0
- package/dist/elevenlabs-voice.d.cts.map +1 -0
- package/dist/elevenlabs-voice.d.ts +72 -0
- package/dist/elevenlabs-voice.d.ts.map +1 -0
- package/dist/elevenlabs-voice.js +1048 -0
- package/dist/elevenlabs-voice.js.map +1 -0
- package/dist/embeddings.cjs +26 -5
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +27 -6
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +3 -3
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.js +4 -4
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +28 -4
- package/dist/fal.cjs.map +1 -1
- package/dist/fal.d.cts +16 -1
- package/dist/fal.d.cts.map +1 -1
- package/dist/fal.d.ts +16 -1
- package/dist/fal.d.ts.map +1 -1
- package/dist/fal.js +28 -5
- package/dist/fal.js.map +1 -1
- package/dist/files.cjs +1069 -0
- package/dist/files.cjs.map +1 -0
- package/dist/files.d.cts +68 -0
- package/dist/files.d.cts.map +1 -0
- package/dist/files.d.ts +68 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +1056 -0
- package/dist/files.js.map +1 -0
- package/dist/fine-tuning.cjs +1033 -0
- package/dist/fine-tuning.cjs.map +1 -0
- package/dist/fine-tuning.d.cts +96 -0
- package/dist/fine-tuning.d.cts.map +1 -0
- package/dist/fine-tuning.d.ts +96 -0
- package/dist/fine-tuning.d.ts.map +1 -0
- package/dist/fine-tuning.js +1026 -0
- package/dist/fine-tuning.js.map +1 -0
- package/dist/fixture-loader-services.cjs +116 -0
- package/dist/fixture-loader-services.cjs.map +1 -0
- package/dist/fixture-loader-services.d.cts +48 -0
- package/dist/fixture-loader-services.d.cts.map +1 -0
- package/dist/fixture-loader-services.d.ts +48 -0
- package/dist/fixture-loader-services.d.ts.map +1 -0
- package/dist/fixture-loader-services.js +114 -0
- package/dist/fixture-loader-services.js.map +1 -0
- package/dist/fixture-loader.cjs +0 -0
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +131 -5
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +131 -5
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +0 -0
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +44 -4
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +46 -6
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +51 -7
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +53 -9
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +19 -8
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +21 -10
- package/dist/gemini.js.map +1 -1
- package/dist/grok-video.cjs +14 -14
- package/dist/grok-video.cjs.map +1 -1
- package/dist/grok-video.js +15 -15
- package/dist/grok-video.js.map +1 -1
- package/dist/helpers.cjs +497 -10
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +38 -2
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +38 -2
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +487 -12
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +57 -125
- package/dist/images.cjs.map +1 -1
- package/dist/images.d.cts.map +1 -1
- package/dist/images.d.ts.map +1 -1
- package/dist/images.js +58 -126
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +71 -9
- package/dist/index.d.cts +17 -7
- package/dist/index.d.ts +17 -7
- package/dist/index.js +21 -11
- package/dist/jest.cjs +24 -12
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +6 -3
- package/dist/jest.d.cts.map +1 -1
- package/dist/jest.d.ts +6 -3
- package/dist/jest.d.ts.map +1 -1
- package/dist/jest.js +25 -13
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +61 -4
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +60 -3
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +60 -3
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +61 -5
- package/dist/journal.js.map +1 -1
- package/dist/live-fixture.cjs +1192 -0
- package/dist/live-fixture.cjs.map +1 -0
- package/dist/live-fixture.js +1187 -0
- package/dist/live-fixture.js.map +1 -0
- package/dist/live-recorder.cjs +256 -0
- package/dist/live-recorder.cjs.map +1 -0
- package/dist/live-recorder.js +254 -0
- package/dist/live-recorder.js.map +1 -0
- package/dist/live-replay.cjs +286 -0
- package/dist/live-replay.cjs.map +1 -0
- package/dist/live-replay.js +285 -0
- package/dist/live-replay.js.map +1 -0
- package/dist/live-sanitize.cjs +196 -0
- package/dist/live-sanitize.cjs.map +1 -0
- package/dist/live-sanitize.js +196 -0
- package/dist/live-sanitize.js.map +1 -0
- package/dist/live-types.d.cts +71 -0
- package/dist/live-types.d.cts.map +1 -0
- package/dist/live-types.d.ts +71 -0
- package/dist/live-types.d.ts.map +1 -0
- package/dist/llmock.cjs +132 -49
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +51 -5
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +51 -5
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +133 -50
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-fakes-mount.cjs +216 -0
- package/dist/mcp-fakes-mount.cjs.map +1 -0
- package/dist/mcp-fakes-mount.js +210 -0
- package/dist/mcp-fakes-mount.js.map +1 -0
- package/dist/mcp-fakes.cjs +1619 -0
- package/dist/mcp-fakes.cjs.map +1 -0
- package/dist/mcp-fakes.d.cts +101 -0
- package/dist/mcp-fakes.d.cts.map +1 -0
- package/dist/mcp-fakes.d.ts +101 -0
- package/dist/mcp-fakes.d.ts.map +1 -0
- package/dist/mcp-fakes.js +1614 -0
- package/dist/mcp-fakes.js.map +1 -0
- package/dist/mcp-handler.cjs +331 -17
- package/dist/mcp-handler.cjs.map +1 -1
- package/dist/mcp-handler.js +330 -18
- package/dist/mcp-handler.js.map +1 -1
- package/dist/mcp-mock.cjs +217 -49
- package/dist/mcp-mock.cjs.map +1 -1
- package/dist/mcp-mock.d.cts +66 -1
- package/dist/mcp-mock.d.cts.map +1 -1
- package/dist/mcp-mock.d.ts +66 -1
- package/dist/mcp-mock.d.ts.map +1 -1
- package/dist/mcp-mock.js +218 -50
- package/dist/mcp-mock.js.map +1 -1
- package/dist/mcp-stub.cjs +5 -1
- package/dist/mcp-stub.d.cts +3 -1
- package/dist/mcp-stub.d.ts +3 -1
- package/dist/mcp-stub.js +3 -1
- package/dist/mcp-types.d.cts +6 -0
- package/dist/mcp-types.d.ts +6 -0
- package/dist/message-text.cjs +192 -0
- package/dist/message-text.cjs.map +1 -0
- package/dist/message-text.d.cts +31 -0
- package/dist/message-text.d.cts.map +1 -0
- package/dist/message-text.d.ts +31 -0
- package/dist/message-text.d.ts.map +1 -0
- package/dist/message-text.js +184 -0
- package/dist/message-text.js.map +1 -0
- package/dist/messages.cjs +49 -7
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +51 -9
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +278 -45
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts +17 -7
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts +17 -7
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +270 -29
- package/dist/metrics.js.map +1 -1
- package/dist/moderation.cjs +10 -1
- package/dist/moderation.cjs.map +1 -1
- package/dist/moderation.d.cts.map +1 -1
- package/dist/moderation.d.ts.map +1 -1
- package/dist/moderation.js +10 -1
- package/dist/moderation.js.map +1 -1
- package/dist/ollama.cjs +10 -6
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +11 -7
- package/dist/ollama.js.map +1 -1
- package/dist/openapi.cjs +3405 -0
- package/dist/openapi.cjs.map +1 -0
- package/dist/openapi.d.cts +30 -0
- package/dist/openapi.d.cts.map +1 -0
- package/dist/openapi.d.ts +30 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +3404 -0
- package/dist/openapi.js.map +1 -0
- package/dist/openrouter-video.cjs +9 -9
- package/dist/openrouter-video.cjs.map +1 -1
- package/dist/openrouter-video.d.cts.map +1 -1
- package/dist/openrouter-video.d.ts.map +1 -1
- package/dist/openrouter-video.js +10 -10
- package/dist/openrouter-video.js.map +1 -1
- package/dist/recorder.cjs +54 -10
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +52 -12
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +47 -7
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +48 -9
- package/dist/responses.js.map +1 -1
- package/dist/route-registry.cjs +951 -0
- package/dist/route-registry.cjs.map +1 -0
- package/dist/route-registry.d.cts +33 -0
- package/dist/route-registry.d.cts.map +1 -0
- package/dist/route-registry.d.ts +33 -0
- package/dist/route-registry.d.ts.map +1 -0
- package/dist/route-registry.js +866 -0
- package/dist/route-registry.js.map +1 -0
- package/dist/router.cjs +21 -34
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts +4 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts +4 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +21 -35
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +1252 -777
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +5 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +5 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1185 -710
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +6 -4
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +7 -5
- package/dist/speech.js.map +1 -1
- package/dist/suite.cjs +1 -1
- package/dist/suite.js +1 -1
- package/dist/transcription.cjs +6 -6
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +7 -7
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs +18 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +320 -17
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +320 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/dist/validate-cli.cjs +983 -0
- package/dist/validate-cli.cjs.map +1 -0
- package/dist/validate-cli.js +982 -0
- package/dist/validate-cli.js.map +1 -0
- package/dist/vector-stores.cjs +1180 -0
- package/dist/vector-stores.cjs.map +1 -0
- package/dist/vector-stores.d.cts +106 -0
- package/dist/vector-stores.d.cts.map +1 -0
- package/dist/vector-stores.d.ts +106 -0
- package/dist/vector-stores.d.ts.map +1 -0
- package/dist/vector-stores.js +1164 -0
- package/dist/vector-stores.js.map +1 -0
- package/dist/vector-types.d.cts.map +1 -1
- package/dist/vector-types.d.ts.map +1 -1
- package/dist/veo-video.cjs +13 -13
- package/dist/veo-video.cjs.map +1 -1
- package/dist/veo-video.js +14 -14
- package/dist/veo-video.js.map +1 -1
- package/dist/video.cjs +23 -4
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +24 -5
- package/dist/video.js.map +1 -1
- package/dist/vitest.cjs +23 -11
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +5 -2
- package/dist/vitest.d.cts.map +1 -1
- package/dist/vitest.d.ts +5 -2
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +25 -13
- package/dist/vitest.js.map +1 -1
- package/dist/watcher.cjs +2 -1
- package/dist/watcher.cjs.map +1 -1
- package/dist/watcher.js +2 -1
- package/dist/watcher.js.map +1 -1
- package/dist/ws-framing.cjs +150 -35
- package/dist/ws-framing.cjs.map +1 -1
- package/dist/ws-framing.d.cts +18 -3
- package/dist/ws-framing.d.cts.map +1 -1
- package/dist/ws-framing.d.ts +18 -3
- package/dist/ws-framing.d.ts.map +1 -1
- package/dist/ws-framing.js +151 -36
- package/dist/ws-framing.js.map +1 -1
- package/dist/ws-gemini-live.cjs +2 -2
- package/dist/ws-gemini-live.js +2 -2
- package/dist/ws-live.cjs +401 -0
- package/dist/ws-live.cjs.map +1 -0
- package/dist/ws-live.js +400 -0
- package/dist/ws-live.js.map +1 -0
- package/dist/ws-realtime.cjs +46 -2
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +46 -2
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +16 -4
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.js +16 -4
- package/dist/ws-responses.js.map +1 -1
- package/dist/ws-upstream.cjs +118 -0
- package/dist/ws-upstream.cjs.map +1 -0
- package/dist/ws-upstream.js +115 -0
- package/dist/ws-upstream.js.map +1 -0
- package/fixtures/openai-live-client.json +390 -0
- package/fixtures/openai-live-managed.json +709 -0
- package/package.json +4 -1
package/dist/ws-framing.cjs
CHANGED
|
@@ -10,7 +10,7 @@ let node_events = require("node:events");
|
|
|
10
10
|
* Supports text frames, ping/pong, close handshake, and client frame unmasking.
|
|
11
11
|
* Designed for a mock server — no extensions, no binary frames, no compression.
|
|
12
12
|
*/
|
|
13
|
-
const WS_GUID = "258EAFA5-E914-47DA-95CA-
|
|
13
|
+
const WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
14
14
|
const OP_CONTINUATION = 0;
|
|
15
15
|
const OP_TEXT = 1;
|
|
16
16
|
const OP_CLOSE = 8;
|
|
@@ -21,16 +21,39 @@ var WebSocketConnection = class extends node_events.EventEmitter {
|
|
|
21
21
|
buffer = Buffer.alloc(0);
|
|
22
22
|
closed = false;
|
|
23
23
|
fragments = [];
|
|
24
|
-
|
|
24
|
+
fragmentBytes = 0;
|
|
25
|
+
fragmented = false;
|
|
26
|
+
pendingWriteBytes = 0;
|
|
27
|
+
pendingWrites = /* @__PURE__ */ new Set();
|
|
28
|
+
limits;
|
|
29
|
+
constructor(socket, limits = {}) {
|
|
25
30
|
super();
|
|
31
|
+
for (const value of [
|
|
32
|
+
limits.maxMessageBytes,
|
|
33
|
+
limits.maxBufferedBytes,
|
|
34
|
+
limits.maxWriteBytes
|
|
35
|
+
]) if (value !== void 0 && (!Number.isSafeInteger(value) || value <= 0)) throw new RangeError("WebSocket limits must be positive safe integers");
|
|
36
|
+
this.limits = { ...limits };
|
|
26
37
|
this.socket = socket;
|
|
27
38
|
socket.on("data", (data) => {
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
if (data.length > 0 && !this.closed) this.emit("activity");
|
|
40
|
+
let offset = 0;
|
|
41
|
+
while (offset < data.length && !this.closed) {
|
|
42
|
+
const room = (this.limits.maxBufferedBytes ?? Infinity) - this.fragmentBytes - this.buffer.length;
|
|
43
|
+
if (room <= 0) {
|
|
44
|
+
this.close(1009, "WebSocket buffer limit exceeded");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const end = offset + Math.min(room, data.length - offset);
|
|
48
|
+
this.buffer = Buffer.concat([this.buffer, data.subarray(offset, end)]);
|
|
49
|
+
offset = end;
|
|
50
|
+
this.parseFrames();
|
|
51
|
+
}
|
|
30
52
|
});
|
|
31
53
|
socket.on("close", () => {
|
|
32
54
|
if (!this.closed) {
|
|
33
55
|
this.closed = true;
|
|
56
|
+
this.release(/* @__PURE__ */ new Error("WebSocket closed"));
|
|
34
57
|
this.emit("close", 1006, "Connection lost");
|
|
35
58
|
}
|
|
36
59
|
});
|
|
@@ -40,13 +63,60 @@ var WebSocketConnection = class extends node_events.EventEmitter {
|
|
|
40
63
|
}
|
|
41
64
|
send(data) {
|
|
42
65
|
if (this.closed) return;
|
|
43
|
-
|
|
44
|
-
|
|
66
|
+
this.sendAsync(data).catch(() => {});
|
|
67
|
+
}
|
|
68
|
+
sendAsync(data, signal) {
|
|
69
|
+
if (this.closed || this.socket.destroyed) return Promise.reject(/* @__PURE__ */ new Error("WebSocket closed"));
|
|
70
|
+
if (signal?.aborted) return Promise.reject(/* @__PURE__ */ new Error("WebSocket send aborted"));
|
|
71
|
+
const length = Buffer.byteLength(data);
|
|
72
|
+
const frameBytes = length + (length < 126 ? 2 : length < 65536 ? 4 : 10) + (this.limits.maskOutgoing ? 4 : 0);
|
|
73
|
+
if (frameBytes > (this.limits.maxWriteBytes ?? Infinity) - Math.max(this.pendingWriteBytes, this.socket.writableLength)) {
|
|
74
|
+
this.close(1009, "WebSocket write limit exceeded");
|
|
75
|
+
return Promise.reject(/* @__PURE__ */ new Error("WebSocket write limit exceeded"));
|
|
76
|
+
}
|
|
77
|
+
this.pendingWriteBytes += frameBytes;
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
let settled = false;
|
|
80
|
+
const finish = (error) => {
|
|
81
|
+
if (settled) return;
|
|
82
|
+
settled = true;
|
|
83
|
+
this.pendingWriteBytes -= frameBytes;
|
|
84
|
+
this.pendingWrites.delete(fail);
|
|
85
|
+
signal?.removeEventListener("abort", abort);
|
|
86
|
+
if (error) reject(error);
|
|
87
|
+
else resolve();
|
|
88
|
+
};
|
|
89
|
+
const fail = (error) => finish(error);
|
|
90
|
+
const abort = () => {
|
|
91
|
+
finish(/* @__PURE__ */ new Error("WebSocket send aborted"));
|
|
92
|
+
this.destroy();
|
|
93
|
+
};
|
|
94
|
+
this.pendingWrites.add(fail);
|
|
95
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
96
|
+
try {
|
|
97
|
+
this.socket.write(this.encodeFrame(OP_TEXT, Buffer.from(data)), (error) => {
|
|
98
|
+
if (error) finish(/* @__PURE__ */ new Error("WebSocket write failed"));
|
|
99
|
+
else finish();
|
|
100
|
+
});
|
|
101
|
+
} catch {
|
|
102
|
+
finish(/* @__PURE__ */ new Error("WebSocket write failed"));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
release(error) {
|
|
107
|
+
this.buffer = Buffer.alloc(0);
|
|
108
|
+
this.fragments = [];
|
|
109
|
+
this.fragmentBytes = 0;
|
|
110
|
+
for (const fail of this.pendingWrites) fail(error);
|
|
45
111
|
}
|
|
46
112
|
close(code = 1e3, reason = "") {
|
|
47
113
|
if (this.closed) return;
|
|
48
114
|
this.closed = true;
|
|
49
|
-
|
|
115
|
+
this.release(/* @__PURE__ */ new Error("WebSocket closed"));
|
|
116
|
+
const encodedReason = Buffer.from(reason, "utf-8");
|
|
117
|
+
let reasonEnd = Math.min(encodedReason.length, 123);
|
|
118
|
+
while (reasonEnd < encodedReason.length && (encodedReason[reasonEnd] & 192) === 128) reasonEnd--;
|
|
119
|
+
const reasonBuf = encodedReason.subarray(0, reasonEnd);
|
|
50
120
|
const payload = Buffer.alloc(2 + reasonBuf.length);
|
|
51
121
|
payload.writeUInt16BE(code, 0);
|
|
52
122
|
reasonBuf.copy(payload, 2);
|
|
@@ -59,40 +129,45 @@ var WebSocketConnection = class extends node_events.EventEmitter {
|
|
|
59
129
|
destroy() {
|
|
60
130
|
if (this.closed) return;
|
|
61
131
|
this.closed = true;
|
|
132
|
+
this.release(/* @__PURE__ */ new Error("WebSocket destroyed"));
|
|
62
133
|
if (!this.socket.destroyed) this.socket.destroy();
|
|
63
134
|
this.emit("close", 1006, "Connection destroyed");
|
|
64
135
|
}
|
|
65
136
|
get isClosed() {
|
|
66
137
|
return this.closed;
|
|
67
138
|
}
|
|
139
|
+
encodeFrame(opcode, payload) {
|
|
140
|
+
const length = payload.length;
|
|
141
|
+
const headerSize = length < 126 ? 2 : length < 65536 ? 4 : 10;
|
|
142
|
+
const maskSize = this.limits.maskOutgoing ? 4 : 0;
|
|
143
|
+
const frame = Buffer.allocUnsafe(headerSize + maskSize + length);
|
|
144
|
+
frame[0] = 128 | opcode;
|
|
145
|
+
frame[1] = (maskSize ? 128 : 0) | (length < 126 ? length : length < 65536 ? 126 : 127);
|
|
146
|
+
if (headerSize === 4) frame.writeUInt16BE(length, 2);
|
|
147
|
+
if (headerSize === 10) frame.writeBigUInt64BE(BigInt(length), 2);
|
|
148
|
+
if (maskSize) {
|
|
149
|
+
const mask = (0, node_crypto.randomBytes)(4);
|
|
150
|
+
mask.copy(frame, headerSize);
|
|
151
|
+
for (let i = 0; i < length; i++) frame[headerSize + 4 + i] = payload[i] ^ mask[i % 4];
|
|
152
|
+
} else payload.copy(frame, headerSize);
|
|
153
|
+
return frame;
|
|
154
|
+
}
|
|
68
155
|
writeFrame(opcode, payload) {
|
|
69
156
|
if (this.socket.destroyed) return;
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
if (length
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
} else if (length < 65536) {
|
|
77
|
-
header = Buffer.alloc(4);
|
|
78
|
-
header[0] = 128 | opcode;
|
|
79
|
-
header[1] = 126;
|
|
80
|
-
header.writeUInt16BE(length, 2);
|
|
81
|
-
} else {
|
|
82
|
-
header = Buffer.alloc(10);
|
|
83
|
-
header[0] = 128 | opcode;
|
|
84
|
-
header[1] = 127;
|
|
85
|
-
header.writeUInt32BE(0, 2);
|
|
86
|
-
header.writeUInt32BE(length, 6);
|
|
87
|
-
}
|
|
88
|
-
try {
|
|
89
|
-
this.socket.write(Buffer.concat([header, payload]));
|
|
90
|
-
} catch (err) {
|
|
91
|
-
if (!this.socket.destroyed) {
|
|
92
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
93
|
-
console.error(`[LLMock] Unexpected writeFrame error: ${msg}`);
|
|
157
|
+
const available = (this.limits.maxWriteBytes ?? Infinity) - Math.max(this.pendingWriteBytes, this.socket.writableLength);
|
|
158
|
+
const headerBytes = 2 + (this.limits.maskOutgoing ? 4 : 0);
|
|
159
|
+
if (opcode === OP_CLOSE && payload.length + headerBytes > available) {
|
|
160
|
+
if (available < headerBytes + 2) {
|
|
161
|
+
this.socket.destroy();
|
|
162
|
+
return;
|
|
94
163
|
}
|
|
164
|
+
payload = payload.subarray(0, 2);
|
|
165
|
+
} else if (payload.length + headerBytes > available) {
|
|
166
|
+
this.close(1009, "WebSocket write limit exceeded");
|
|
167
|
+
return;
|
|
95
168
|
}
|
|
169
|
+
const frame = this.encodeFrame(opcode, payload);
|
|
170
|
+
this.socket.write(frame);
|
|
96
171
|
}
|
|
97
172
|
parseFrames() {
|
|
98
173
|
while (this.buffer.length >= 2 && !this.closed) {
|
|
@@ -101,6 +176,17 @@ var WebSocketConnection = class extends node_events.EventEmitter {
|
|
|
101
176
|
const fin = (byte0 & 128) !== 0;
|
|
102
177
|
const opcode = byte0 & 15;
|
|
103
178
|
const masked = (byte1 & 128) !== 0;
|
|
179
|
+
if (byte0 & 112 || masked === Boolean(this.limits.maskOutgoing) || ![
|
|
180
|
+
OP_CONTINUATION,
|
|
181
|
+
OP_TEXT,
|
|
182
|
+
2,
|
|
183
|
+
OP_CLOSE,
|
|
184
|
+
OP_PING,
|
|
185
|
+
OP_PONG
|
|
186
|
+
].includes(opcode) || opcode >= 8 && (!fin || (byte1 & 127) > 125) || opcode === OP_CONTINUATION && !this.fragmented || opcode === OP_TEXT && this.fragmented) {
|
|
187
|
+
this.close(1002, "Invalid WebSocket frame");
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
104
190
|
let payloadLength = byte1 & 127;
|
|
105
191
|
let offset = 2;
|
|
106
192
|
if (payloadLength === 126) {
|
|
@@ -109,9 +195,30 @@ var WebSocketConnection = class extends node_events.EventEmitter {
|
|
|
109
195
|
offset = 4;
|
|
110
196
|
} else if (payloadLength === 127) {
|
|
111
197
|
if (this.buffer.length < 10) return;
|
|
112
|
-
|
|
198
|
+
const advertised = this.buffer.readBigUInt64BE(2);
|
|
199
|
+
if (advertised & 1n << 63n) {
|
|
200
|
+
this.close(1002, "Invalid WebSocket length");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (advertised > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
204
|
+
this.close(1009, "WebSocket message limit exceeded");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
payloadLength = Number(advertised);
|
|
113
208
|
offset = 10;
|
|
114
209
|
}
|
|
210
|
+
if ((byte1 & 127) === 126 && payloadLength < 126 || (byte1 & 127) === 127 && payloadLength < 65536) {
|
|
211
|
+
this.close(1002, "Invalid WebSocket length");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (opcode < 8 && payloadLength > (this.limits.maxMessageBytes ?? Infinity) - this.fragmentBytes) {
|
|
215
|
+
this.close(1009, "WebSocket message limit exceeded");
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (payloadLength + offset + (masked ? 4 : 0) > (this.limits.maxBufferedBytes ?? Infinity) - this.fragmentBytes) {
|
|
219
|
+
this.close(1009, "WebSocket buffer limit exceeded");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
115
222
|
const totalFrameSize = offset + (masked ? 4 : 0) + payloadLength;
|
|
116
223
|
if (this.buffer.length < totalFrameSize) return;
|
|
117
224
|
let maskKey = null;
|
|
@@ -135,10 +242,15 @@ var WebSocketConnection = class extends node_events.EventEmitter {
|
|
|
135
242
|
}
|
|
136
243
|
if (opcode === OP_PONG) return;
|
|
137
244
|
if (opcode === OP_CLOSE) {
|
|
245
|
+
if (payload.length === 1) {
|
|
246
|
+
this.close(1002, "Invalid WebSocket close");
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
138
249
|
const code = payload.length >= 2 ? payload.readUInt16BE(0) : 1005;
|
|
139
250
|
const reason = payload.length > 2 ? payload.subarray(2).toString("utf-8") : "";
|
|
140
251
|
if (!this.closed) {
|
|
141
252
|
this.closed = true;
|
|
253
|
+
this.release(/* @__PURE__ */ new Error("WebSocket closed"));
|
|
142
254
|
this.writeFrame(OP_CLOSE, payload);
|
|
143
255
|
this.socket.end();
|
|
144
256
|
this.emit("close", code, reason);
|
|
@@ -146,10 +258,13 @@ var WebSocketConnection = class extends node_events.EventEmitter {
|
|
|
146
258
|
return;
|
|
147
259
|
}
|
|
148
260
|
if (opcode === OP_TEXT || opcode === OP_CONTINUATION) {
|
|
149
|
-
this.
|
|
261
|
+
this.fragmented = !fin;
|
|
262
|
+
this.fragmentBytes += payload.length;
|
|
263
|
+
if (payload.length) this.fragments.push(payload);
|
|
150
264
|
if (fin) {
|
|
151
265
|
const message = Buffer.concat(this.fragments).toString("utf-8");
|
|
152
266
|
this.fragments = [];
|
|
267
|
+
this.fragmentBytes = 0;
|
|
153
268
|
this.emit("message", message);
|
|
154
269
|
}
|
|
155
270
|
return;
|
|
@@ -159,7 +274,7 @@ var WebSocketConnection = class extends node_events.EventEmitter {
|
|
|
159
274
|
function computeAcceptKey(wsKey) {
|
|
160
275
|
return (0, node_crypto.createHash)("sha1").update(wsKey + WS_GUID).digest("base64");
|
|
161
276
|
}
|
|
162
|
-
function upgradeToWebSocket(req, socket) {
|
|
277
|
+
function upgradeToWebSocket(req, socket, limits) {
|
|
163
278
|
const key = req.headers["sec-websocket-key"];
|
|
164
279
|
if (!key) {
|
|
165
280
|
socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
|
|
@@ -177,7 +292,7 @@ Sec-WebSocket-Accept: ${computeAcceptKey(key)}\r\n`;
|
|
|
177
292
|
}
|
|
178
293
|
responseHeaders += "\r\n";
|
|
179
294
|
socket.write(responseHeaders);
|
|
180
|
-
return new WebSocketConnection(socket);
|
|
295
|
+
return new WebSocketConnection(socket, limits);
|
|
181
296
|
}
|
|
182
297
|
|
|
183
298
|
//#endregion
|
package/dist/ws-framing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-framing.cjs","names":["EventEmitter"],"sources":["../src/ws-framing.ts"],"sourcesContent":["/**\n * Minimal RFC 6455 WebSocket server implementation.\n *\n * Zero dependencies — uses only Node.js builtins (node:crypto, node:events).\n * Supports text frames, ping/pong, close handshake, and client frame unmasking.\n * Designed for a mock server — no extensions, no binary frames, no compression.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { EventEmitter } from \"node:events\";\nimport type * as net from \"node:net\";\nimport type * as http from \"node:http\";\n\nconst WS_GUID = \"258EAFA5-E914-47DA-95CA-5AB5DC799C07\";\n\n// Opcodes\nconst OP_CONTINUATION = 0x0;\nconst OP_TEXT = 0x1;\nconst OP_CLOSE = 0x8;\nconst OP_PING = 0x9;\nconst OP_PONG = 0xa;\n\nexport class WebSocketConnection extends EventEmitter {\n private socket: net.Socket;\n private buffer: Buffer = Buffer.alloc(0);\n private closed = false;\n\n // For fragmented messages (continuation frames)\n private fragments: Buffer[] = [];\n\n constructor(socket: net.Socket) {\n super();\n this.socket = socket;\n\n socket.on(\"data\", (data: Buffer) => {\n this.buffer = Buffer.concat([this.buffer, data]);\n this.parseFrames();\n });\n\n socket.on(\"close\", () => {\n if (!this.closed) {\n this.closed = true;\n this.emit(\"close\", 1006, \"Connection lost\");\n }\n });\n\n socket.on(\"error\", (err: Error) => {\n this.emit(\"error\", err);\n });\n }\n\n send(data: string): void {\n if (this.closed) return;\n const payload = Buffer.from(data, \"utf-8\");\n this.writeFrame(OP_TEXT, payload);\n }\n\n close(code = 1000, reason = \"\"): void {\n if (this.closed) return;\n this.closed = true;\n\n const reasonBuf = Buffer.from(reason, \"utf-8\");\n const payload = Buffer.alloc(2 + reasonBuf.length);\n payload.writeUInt16BE(code, 0);\n reasonBuf.copy(payload, 2);\n this.writeFrame(OP_CLOSE, payload);\n\n // Give the client a moment to receive the close frame before destroying.\n // If writeFrame failed (socket already destroyed), this is a no-op.\n setTimeout(() => {\n if (!this.socket.destroyed) {\n this.socket.destroy();\n }\n // Emit close event for server-initiated closes so listeners\n // (e.g. activeConnections.delete) always fire.\n this.emit(\"close\", code, reason);\n }, 100);\n }\n\n destroy(): void {\n if (this.closed) return;\n this.closed = true;\n if (!this.socket.destroyed) {\n this.socket.destroy();\n }\n this.emit(\"close\", 1006, \"Connection destroyed\");\n }\n\n get isClosed(): boolean {\n return this.closed;\n }\n\n private writeFrame(opcode: number, payload: Buffer): void {\n if (this.socket.destroyed) return;\n\n // Server-to-client frames are NOT masked (per RFC 6455 §5.1)\n const length = payload.length;\n let header: Buffer;\n\n if (length < 126) {\n header = Buffer.alloc(2);\n header[0] = 0x80 | opcode; // FIN + opcode\n header[1] = length;\n } else if (length < 65536) {\n header = Buffer.alloc(4);\n header[0] = 0x80 | opcode;\n header[1] = 126;\n header.writeUInt16BE(length, 2);\n } else {\n header = Buffer.alloc(10);\n header[0] = 0x80 | opcode;\n header[1] = 127;\n header.writeUInt32BE(0, 2);\n header.writeUInt32BE(length, 6);\n }\n\n try {\n this.socket.write(Buffer.concat([header, payload]));\n } catch (err: unknown) {\n // Expected when socket is destroyed between our check and write.\n // Log unexpected errors so they don't vanish silently.\n if (!this.socket.destroyed) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[LLMock] Unexpected writeFrame error: ${msg}`);\n }\n }\n }\n\n private parseFrames(): void {\n while (this.buffer.length >= 2 && !this.closed) {\n const byte0 = this.buffer[0];\n const byte1 = this.buffer[1];\n\n const fin = (byte0 & 0x80) !== 0;\n const opcode = byte0 & 0x0f;\n const masked = (byte1 & 0x80) !== 0;\n let payloadLength = byte1 & 0x7f;\n let offset = 2;\n\n if (payloadLength === 126) {\n if (this.buffer.length < 4) return; // need more data\n payloadLength = this.buffer.readUInt16BE(2);\n offset = 4;\n } else if (payloadLength === 127) {\n if (this.buffer.length < 10) return;\n // Read lower 32 bits (upper 32 should be 0 for reasonable payloads)\n payloadLength = this.buffer.readUInt32BE(6) + this.buffer.readUInt32BE(2) * 0x100000000;\n offset = 10;\n }\n\n const maskSize = masked ? 4 : 0;\n const totalFrameSize = offset + maskSize + payloadLength;\n\n if (this.buffer.length < totalFrameSize) return; // need more data\n\n let maskKey: Buffer | null = null;\n if (masked) {\n maskKey = this.buffer.subarray(offset, offset + 4);\n offset += 4;\n }\n\n let payload = this.buffer.subarray(offset, offset + payloadLength);\n\n // Unmask client payload\n if (maskKey) {\n payload = Buffer.from(payload); // copy before mutating\n for (let i = 0; i < payload.length; i++) {\n payload[i] ^= maskKey[i % 4];\n }\n }\n\n // Consume the frame from the buffer\n this.buffer = this.buffer.subarray(totalFrameSize);\n\n this.handleFrame(fin, opcode, payload);\n }\n }\n\n private handleFrame(fin: boolean, opcode: number, payload: Buffer): void {\n // Control frames (opcode >= 0x8) must not be fragmented\n if (opcode === OP_PING) {\n this.writeFrame(OP_PONG, payload);\n return;\n }\n\n if (opcode === OP_PONG) {\n // Ignore unsolicited pongs\n return;\n }\n\n if (opcode === OP_CLOSE) {\n const code = payload.length >= 2 ? payload.readUInt16BE(0) : 1005;\n const reason = payload.length > 2 ? payload.subarray(2).toString(\"utf-8\") : \"\";\n\n if (!this.closed) {\n this.closed = true;\n // Echo close frame back\n this.writeFrame(OP_CLOSE, payload);\n this.socket.end();\n this.emit(\"close\", code, reason);\n }\n // If already closed (server-initiated or duplicate), ignore — the\n // close event was already emitted by close() or the first OP_CLOSE.\n return;\n }\n\n // Text or continuation frames\n if (opcode === OP_TEXT || opcode === OP_CONTINUATION) {\n this.fragments.push(payload);\n\n if (fin) {\n const message = Buffer.concat(this.fragments).toString(\"utf-8\");\n this.fragments = [];\n this.emit(\"message\", message);\n }\n // If !fin, wait for more continuation frames\n return;\n }\n\n // Binary or unknown — just ignore for a mock server\n }\n}\n\nexport function computeAcceptKey(wsKey: string): string {\n return createHash(\"sha1\")\n .update(wsKey + WS_GUID)\n .digest(\"base64\");\n}\n\nexport function upgradeToWebSocket(\n req: http.IncomingMessage,\n socket: net.Socket,\n): WebSocketConnection {\n const key = req.headers[\"sec-websocket-key\"];\n if (!key) {\n socket.write(\"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n socket.destroy();\n throw new Error(\"Missing Sec-WebSocket-Key header\");\n }\n\n const acceptKey = computeAcceptKey(key);\n\n let responseHeaders =\n \"HTTP/1.1 101 Switching Protocols\\r\\n\" +\n \"Upgrade: websocket\\r\\n\" +\n \"Connection: Upgrade\\r\\n\" +\n `Sec-WebSocket-Accept: ${acceptKey}\\r\\n`;\n\n // Echo back requested subprotocol if present\n const protocol = req.headers[\"sec-websocket-protocol\"];\n if (protocol) {\n // Take the first offered protocol\n const first = protocol.split(\",\")[0].trim();\n responseHeaders += `Sec-WebSocket-Protocol: ${first}\\r\\n`;\n }\n\n responseHeaders += \"\\r\\n\";\n\n socket.write(responseHeaders);\n\n return new WebSocketConnection(socket);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ws-framing.cjs","names":["EventEmitter"],"sources":["../src/ws-framing.ts"],"sourcesContent":["/**\n * Minimal RFC 6455 WebSocket server implementation.\n *\n * Zero dependencies — uses only Node.js builtins (node:crypto, node:events).\n * Supports text frames, ping/pong, close handshake, and client frame unmasking.\n * Designed for a mock server — no extensions, no binary frames, no compression.\n */\n\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { EventEmitter } from \"node:events\";\nimport type * as net from \"node:net\";\nimport type * as http from \"node:http\";\n\nconst WS_GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\";\n\n// Opcodes\nconst OP_CONTINUATION = 0x0;\nconst OP_TEXT = 0x1;\nconst OP_CLOSE = 0x8;\nconst OP_PING = 0x9;\nconst OP_PONG = 0xa;\n\nexport interface WebSocketLimits {\n maxMessageBytes?: number;\n maxBufferedBytes?: number;\n maxWriteBytes?: number;\n /** Client role: mask outgoing frames and require unmasked incoming frames. */\n maskOutgoing?: boolean;\n}\n\nexport class WebSocketConnection extends EventEmitter {\n private socket: net.Socket;\n private buffer: Buffer = Buffer.alloc(0);\n private closed = false;\n\n // For fragmented messages (continuation frames)\n private fragments: Buffer[] = [];\n\n private fragmentBytes = 0;\n private fragmented = false;\n private pendingWriteBytes = 0;\n private pendingWrites = new Set<(error: Error) => void>();\n private limits: WebSocketLimits;\n\n constructor(socket: net.Socket, limits: WebSocketLimits = {}) {\n super();\n for (const value of [limits.maxMessageBytes, limits.maxBufferedBytes, limits.maxWriteBytes]) {\n if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {\n throw new RangeError(\"WebSocket limits must be positive safe integers\");\n }\n }\n this.limits = { ...limits };\n this.socket = socket;\n\n socket.on(\"data\", (data: Buffer) => {\n // Byte-idle consumers must see partial frames and control traffic too.\n // Emit no payload, and notify before parsing can dispatch a message.\n if (data.length > 0 && !this.closed) this.emit(\"activity\");\n let offset = 0;\n while (offset < data.length && !this.closed) {\n const room =\n (this.limits.maxBufferedBytes ?? Infinity) - this.fragmentBytes - this.buffer.length;\n if (room <= 0) {\n this.close(1009, \"WebSocket buffer limit exceeded\");\n return;\n }\n const end = offset + Math.min(room, data.length - offset);\n this.buffer = Buffer.concat([this.buffer, data.subarray(offset, end)]);\n offset = end;\n this.parseFrames();\n }\n });\n\n socket.on(\"close\", () => {\n if (!this.closed) {\n this.closed = true;\n this.release(new Error(\"WebSocket closed\"));\n this.emit(\"close\", 1006, \"Connection lost\");\n }\n });\n\n socket.on(\"error\", (err: Error) => {\n this.emit(\"error\", err);\n });\n }\n\n send(data: string): void {\n if (this.closed) return;\n void this.sendAsync(data).catch(() => {\n /* Legacy fire-and-forget API. */\n });\n }\n\n sendAsync(data: string, signal?: AbortSignal): Promise<void> {\n if (this.closed || this.socket.destroyed) return Promise.reject(new Error(\"WebSocket closed\"));\n if (signal?.aborted) return Promise.reject(new Error(\"WebSocket send aborted\"));\n const length = Buffer.byteLength(data);\n const frameBytes =\n length + (length < 126 ? 2 : length < 65536 ? 4 : 10) + (this.limits.maskOutgoing ? 4 : 0);\n if (\n frameBytes >\n (this.limits.maxWriteBytes ?? Infinity) -\n Math.max(this.pendingWriteBytes, this.socket.writableLength)\n ) {\n this.close(1009, \"WebSocket write limit exceeded\");\n return Promise.reject(new Error(\"WebSocket write limit exceeded\"));\n }\n this.pendingWriteBytes += frameBytes;\n return new Promise<void>((resolve, reject) => {\n let settled = false;\n const finish = (error?: Error) => {\n if (settled) return;\n settled = true;\n this.pendingWriteBytes -= frameBytes;\n this.pendingWrites.delete(fail);\n signal?.removeEventListener(\"abort\", abort);\n if (error) reject(error);\n else resolve();\n };\n const fail = (error: Error) => finish(error);\n const abort = () => {\n finish(new Error(\"WebSocket send aborted\"));\n this.destroy();\n };\n this.pendingWrites.add(fail);\n signal?.addEventListener(\"abort\", abort, { once: true });\n try {\n this.socket.write(this.encodeFrame(OP_TEXT, Buffer.from(data)), (error?: Error | null) => {\n if (error) finish(new Error(\"WebSocket write failed\"));\n else finish();\n });\n } catch {\n finish(new Error(\"WebSocket write failed\"));\n }\n });\n }\n\n private release(error: Error): void {\n this.buffer = Buffer.alloc(0);\n this.fragments = [];\n this.fragmentBytes = 0;\n for (const fail of this.pendingWrites) fail(error);\n }\n\n close(code = 1000, reason = \"\"): void {\n if (this.closed) return;\n this.closed = true;\n this.release(new Error(\"WebSocket closed\"));\n\n const encodedReason = Buffer.from(reason, \"utf-8\");\n let reasonEnd = Math.min(encodedReason.length, 123);\n // A continuation byte at the cut means its code point straddles the limit.\n while (reasonEnd < encodedReason.length && (encodedReason[reasonEnd] & 0xc0) === 0x80) {\n reasonEnd--;\n }\n const reasonBuf = encodedReason.subarray(0, reasonEnd);\n const payload = Buffer.alloc(2 + reasonBuf.length);\n payload.writeUInt16BE(code, 0);\n reasonBuf.copy(payload, 2);\n this.writeFrame(OP_CLOSE, payload);\n\n // Give the client a moment to receive the close frame before destroying.\n // If writeFrame failed (socket already destroyed), this is a no-op.\n setTimeout(() => {\n if (!this.socket.destroyed) {\n this.socket.destroy();\n }\n // Emit close event for server-initiated closes so listeners\n // (e.g. activeConnections.delete) always fire.\n this.emit(\"close\", code, reason);\n }, 100);\n }\n\n destroy(): void {\n if (this.closed) return;\n this.closed = true;\n this.release(new Error(\"WebSocket destroyed\"));\n if (!this.socket.destroyed) {\n this.socket.destroy();\n }\n this.emit(\"close\", 1006, \"Connection destroyed\");\n }\n\n get isClosed(): boolean {\n return this.closed;\n }\n\n private encodeFrame(opcode: number, payload: Buffer): Buffer {\n const length = payload.length;\n const headerSize = length < 126 ? 2 : length < 65536 ? 4 : 10;\n const maskSize = this.limits.maskOutgoing ? 4 : 0;\n const frame = Buffer.allocUnsafe(headerSize + maskSize + length);\n frame[0] = 0x80 | opcode;\n frame[1] = (maskSize ? 0x80 : 0) | (length < 126 ? length : length < 65536 ? 126 : 127);\n if (headerSize === 4) frame.writeUInt16BE(length, 2);\n if (headerSize === 10) frame.writeBigUInt64BE(BigInt(length), 2);\n if (maskSize) {\n const mask = randomBytes(4);\n mask.copy(frame, headerSize);\n for (let i = 0; i < length; i++) frame[headerSize + 4 + i] = payload[i] ^ mask[i % 4];\n } else payload.copy(frame, headerSize);\n return frame;\n }\n\n private writeFrame(opcode: number, payload: Buffer): void {\n if (this.socket.destroyed) return;\n const available =\n (this.limits.maxWriteBytes ?? Infinity) -\n Math.max(this.pendingWriteBytes, this.socket.writableLength);\n const headerBytes = 2 + (this.limits.maskOutgoing ? 4 : 0);\n if (opcode === OP_CLOSE && payload.length + headerBytes > available) {\n // Keep even the final control frame inside the configured socket budget.\n if (available < headerBytes + 2) {\n this.socket.destroy();\n return;\n }\n payload = payload.subarray(0, 2);\n } else if (payload.length + headerBytes > available) {\n this.close(1009, \"WebSocket write limit exceeded\");\n return;\n }\n const frame = this.encodeFrame(opcode, payload);\n this.socket.write(frame);\n }\n\n private parseFrames(): void {\n while (this.buffer.length >= 2 && !this.closed) {\n const byte0 = this.buffer[0];\n const byte1 = this.buffer[1];\n\n const fin = (byte0 & 0x80) !== 0;\n const opcode = byte0 & 0x0f;\n const masked = (byte1 & 0x80) !== 0;\n if (\n byte0 & 0x70 ||\n masked === Boolean(this.limits.maskOutgoing) ||\n ![OP_CONTINUATION, OP_TEXT, 2, OP_CLOSE, OP_PING, OP_PONG].includes(opcode) ||\n (opcode >= 8 && (!fin || (byte1 & 0x7f) > 125)) ||\n (opcode === OP_CONTINUATION && !this.fragmented) ||\n (opcode === OP_TEXT && this.fragmented)\n ) {\n this.close(1002, \"Invalid WebSocket frame\");\n return;\n }\n let payloadLength = byte1 & 0x7f;\n let offset = 2;\n\n if (payloadLength === 126) {\n if (this.buffer.length < 4) return; // need more data\n payloadLength = this.buffer.readUInt16BE(2);\n offset = 4;\n } else if (payloadLength === 127) {\n if (this.buffer.length < 10) return;\n const advertised = this.buffer.readBigUInt64BE(2);\n if (advertised & (1n << 63n)) {\n this.close(1002, \"Invalid WebSocket length\");\n return;\n }\n if (advertised > BigInt(Number.MAX_SAFE_INTEGER)) {\n this.close(1009, \"WebSocket message limit exceeded\");\n return;\n }\n payloadLength = Number(advertised);\n offset = 10;\n }\n\n if (\n ((byte1 & 0x7f) === 126 && payloadLength < 126) ||\n ((byte1 & 0x7f) === 127 && payloadLength < 65536)\n ) {\n this.close(1002, \"Invalid WebSocket length\");\n return;\n }\n if (\n opcode < 8 &&\n payloadLength > (this.limits.maxMessageBytes ?? Infinity) - this.fragmentBytes\n ) {\n this.close(1009, \"WebSocket message limit exceeded\");\n return;\n }\n if (\n payloadLength + offset + (masked ? 4 : 0) >\n (this.limits.maxBufferedBytes ?? Infinity) - this.fragmentBytes\n ) {\n this.close(1009, \"WebSocket buffer limit exceeded\");\n return;\n }\n const maskSize = masked ? 4 : 0;\n const totalFrameSize = offset + maskSize + payloadLength;\n\n if (this.buffer.length < totalFrameSize) return; // need more data\n\n let maskKey: Buffer | null = null;\n if (masked) {\n maskKey = this.buffer.subarray(offset, offset + 4);\n offset += 4;\n }\n\n let payload = this.buffer.subarray(offset, offset + payloadLength);\n\n // Unmask client payload\n if (maskKey) {\n payload = Buffer.from(payload); // copy before mutating\n for (let i = 0; i < payload.length; i++) {\n payload[i] ^= maskKey[i % 4];\n }\n }\n\n // Consume the frame from the buffer\n this.buffer = this.buffer.subarray(totalFrameSize);\n\n this.handleFrame(fin, opcode, payload);\n }\n }\n\n private handleFrame(fin: boolean, opcode: number, payload: Buffer): void {\n // Control frames (opcode >= 0x8) must not be fragmented\n if (opcode === OP_PING) {\n this.writeFrame(OP_PONG, payload);\n return;\n }\n\n if (opcode === OP_PONG) {\n // Ignore unsolicited pongs\n return;\n }\n\n if (opcode === OP_CLOSE) {\n if (payload.length === 1) {\n this.close(1002, \"Invalid WebSocket close\");\n return;\n }\n const code = payload.length >= 2 ? payload.readUInt16BE(0) : 1005;\n const reason = payload.length > 2 ? payload.subarray(2).toString(\"utf-8\") : \"\";\n\n if (!this.closed) {\n this.closed = true;\n this.release(new Error(\"WebSocket closed\"));\n // Echo close frame back\n this.writeFrame(OP_CLOSE, payload);\n this.socket.end();\n this.emit(\"close\", code, reason);\n }\n // If already closed (server-initiated or duplicate), ignore — the\n // close event was already emitted by close() or the first OP_CLOSE.\n return;\n }\n\n // Text or continuation frames\n if (opcode === OP_TEXT || opcode === OP_CONTINUATION) {\n this.fragmented = !fin;\n this.fragmentBytes += payload.length;\n if (payload.length) this.fragments.push(payload);\n\n if (fin) {\n const message = Buffer.concat(this.fragments).toString(\"utf-8\");\n this.fragments = [];\n this.fragmentBytes = 0;\n this.emit(\"message\", message);\n }\n // If !fin, wait for more continuation frames\n return;\n }\n\n // Binary or unknown — just ignore for a mock server\n }\n}\n\nexport function computeAcceptKey(wsKey: string): string {\n return createHash(\"sha1\")\n .update(wsKey + WS_GUID)\n .digest(\"base64\");\n}\n\nexport function upgradeToWebSocket(\n req: http.IncomingMessage,\n socket: net.Socket,\n limits?: WebSocketLimits,\n): WebSocketConnection {\n const key = req.headers[\"sec-websocket-key\"];\n if (!key) {\n socket.write(\"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n socket.destroy();\n throw new Error(\"Missing Sec-WebSocket-Key header\");\n }\n\n const acceptKey = computeAcceptKey(key);\n\n let responseHeaders =\n \"HTTP/1.1 101 Switching Protocols\\r\\n\" +\n \"Upgrade: websocket\\r\\n\" +\n \"Connection: Upgrade\\r\\n\" +\n `Sec-WebSocket-Accept: ${acceptKey}\\r\\n`;\n\n // Echo back requested subprotocol if present\n const protocol = req.headers[\"sec-websocket-protocol\"];\n if (protocol) {\n // Take the first offered protocol\n const first = protocol.split(\",\")[0].trim();\n responseHeaders += `Sec-WebSocket-Protocol: ${first}\\r\\n`;\n }\n\n responseHeaders += \"\\r\\n\";\n\n socket.write(responseHeaders);\n\n return new WebSocketConnection(socket, limits);\n}\n"],"mappings":"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"}
|
package/dist/ws-framing.d.cts
CHANGED
|
@@ -4,23 +4,38 @@ import { EventEmitter } from "node:events";
|
|
|
4
4
|
|
|
5
5
|
//#region src/ws-framing.d.ts
|
|
6
6
|
|
|
7
|
+
interface WebSocketLimits {
|
|
8
|
+
maxMessageBytes?: number;
|
|
9
|
+
maxBufferedBytes?: number;
|
|
10
|
+
maxWriteBytes?: number;
|
|
11
|
+
/** Client role: mask outgoing frames and require unmasked incoming frames. */
|
|
12
|
+
maskOutgoing?: boolean;
|
|
13
|
+
}
|
|
7
14
|
declare class WebSocketConnection extends EventEmitter {
|
|
8
15
|
private socket;
|
|
9
16
|
private buffer;
|
|
10
17
|
private closed;
|
|
11
18
|
private fragments;
|
|
12
|
-
|
|
19
|
+
private fragmentBytes;
|
|
20
|
+
private fragmented;
|
|
21
|
+
private pendingWriteBytes;
|
|
22
|
+
private pendingWrites;
|
|
23
|
+
private limits;
|
|
24
|
+
constructor(socket: net.Socket, limits?: WebSocketLimits);
|
|
13
25
|
send(data: string): void;
|
|
26
|
+
sendAsync(data: string, signal?: AbortSignal): Promise<void>;
|
|
27
|
+
private release;
|
|
14
28
|
close(code?: number, reason?: string): void;
|
|
15
29
|
destroy(): void;
|
|
16
30
|
get isClosed(): boolean;
|
|
31
|
+
private encodeFrame;
|
|
17
32
|
private writeFrame;
|
|
18
33
|
private parseFrames;
|
|
19
34
|
private handleFrame;
|
|
20
35
|
}
|
|
21
36
|
declare function computeAcceptKey(wsKey: string): string;
|
|
22
|
-
declare function upgradeToWebSocket(req: http$1.IncomingMessage, socket: net.Socket): WebSocketConnection;
|
|
37
|
+
declare function upgradeToWebSocket(req: http$1.IncomingMessage, socket: net.Socket, limits?: WebSocketLimits): WebSocketConnection;
|
|
23
38
|
//# sourceMappingURL=ws-framing.d.ts.map
|
|
24
39
|
//#endregion
|
|
25
|
-
export { WebSocketConnection, computeAcceptKey, upgradeToWebSocket };
|
|
40
|
+
export { WebSocketConnection, WebSocketLimits, computeAcceptKey, upgradeToWebSocket };
|
|
26
41
|
//# sourceMappingURL=ws-framing.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-framing.d.cts","names":[],"sources":["../src/ws-framing.ts"],"sourcesContent":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"ws-framing.d.cts","names":[],"sources":["../src/ws-framing.ts"],"sourcesContent":[],"mappings":";;;;;;AA6FmC,UAvElB,eAAA,CAuEkB;iBAAc,CAAA,EAAA,MAAA;kBA/DR,CAAA,EAAA,MAAA;EAAY,aAAA,CAAA,EAAA,MAAA;EAkVrC;EAMA,YAAA,CAAA,EAAA,OAAkB;;AAC3B,cAzVM,mBAAA,SAA4B,YAAA,CAyV7B;UACE,MAAA;UACH,MAAA;UACR,MAAA;EAAmB,QAAA,SAAA;;;;;;sBA9UA,GAAA,CAAI,iBAAgB;;mCAiDP,cAAc;;;;;;;;;;iBAmRjC,gBAAA;iBAMA,kBAAA,MACT,MAAA,CAAK,yBACF,GAAA,CAAI,iBACH,kBACR"}
|
package/dist/ws-framing.d.ts
CHANGED
|
@@ -4,23 +4,38 @@ import * as net from "node:net";
|
|
|
4
4
|
|
|
5
5
|
//#region src/ws-framing.d.ts
|
|
6
6
|
|
|
7
|
+
interface WebSocketLimits {
|
|
8
|
+
maxMessageBytes?: number;
|
|
9
|
+
maxBufferedBytes?: number;
|
|
10
|
+
maxWriteBytes?: number;
|
|
11
|
+
/** Client role: mask outgoing frames and require unmasked incoming frames. */
|
|
12
|
+
maskOutgoing?: boolean;
|
|
13
|
+
}
|
|
7
14
|
declare class WebSocketConnection extends EventEmitter {
|
|
8
15
|
private socket;
|
|
9
16
|
private buffer;
|
|
10
17
|
private closed;
|
|
11
18
|
private fragments;
|
|
12
|
-
|
|
19
|
+
private fragmentBytes;
|
|
20
|
+
private fragmented;
|
|
21
|
+
private pendingWriteBytes;
|
|
22
|
+
private pendingWrites;
|
|
23
|
+
private limits;
|
|
24
|
+
constructor(socket: net.Socket, limits?: WebSocketLimits);
|
|
13
25
|
send(data: string): void;
|
|
26
|
+
sendAsync(data: string, signal?: AbortSignal): Promise<void>;
|
|
27
|
+
private release;
|
|
14
28
|
close(code?: number, reason?: string): void;
|
|
15
29
|
destroy(): void;
|
|
16
30
|
get isClosed(): boolean;
|
|
31
|
+
private encodeFrame;
|
|
17
32
|
private writeFrame;
|
|
18
33
|
private parseFrames;
|
|
19
34
|
private handleFrame;
|
|
20
35
|
}
|
|
21
36
|
declare function computeAcceptKey(wsKey: string): string;
|
|
22
|
-
declare function upgradeToWebSocket(req: http$1.IncomingMessage, socket: net.Socket): WebSocketConnection;
|
|
37
|
+
declare function upgradeToWebSocket(req: http$1.IncomingMessage, socket: net.Socket, limits?: WebSocketLimits): WebSocketConnection;
|
|
23
38
|
//# sourceMappingURL=ws-framing.d.ts.map
|
|
24
39
|
//#endregion
|
|
25
|
-
export { WebSocketConnection, computeAcceptKey, upgradeToWebSocket };
|
|
40
|
+
export { WebSocketConnection, WebSocketLimits, computeAcceptKey, upgradeToWebSocket };
|
|
26
41
|
//# sourceMappingURL=ws-framing.d.ts.map
|
package/dist/ws-framing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-framing.d.ts","names":[],"sources":["../src/ws-framing.ts"],"sourcesContent":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"ws-framing.d.ts","names":[],"sources":["../src/ws-framing.ts"],"sourcesContent":[],"mappings":";;;;;;AA6FmC,UAvElB,eAAA,CAuEkB;iBAAc,CAAA,EAAA,MAAA;kBA/DR,CAAA,EAAA,MAAA;EAAY,aAAA,CAAA,EAAA,MAAA;EAkVrC;EAMA,YAAA,CAAA,EAAA,OAAkB;;AAC3B,cAzVM,mBAAA,SAA4B,YAAA,CAyV7B;UACE,MAAA;UACH,MAAA;UACR,MAAA;EAAmB,QAAA,SAAA;;;;;;sBA9UA,GAAA,CAAI,iBAAgB;;mCAiDP,cAAc;;;;;;;;;;iBAmRjC,gBAAA;iBAMA,kBAAA,MACT,MAAA,CAAK,yBACF,GAAA,CAAI,iBACH,kBACR"}
|