ventiws 1.0.0-alpha.1 → 1.0.0-alpha.11

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 (2) hide show
  1. package/README.md +160 -0
  2. package/package.json +6 -6
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ <p align="center">
2
+ <img src="misc/ventiws_banner.png" alt="ventiws banner" />
3
+ </p>
4
+
5
+ # ventiws
6
+
7
+ A WebSocket implementation for Node.js with the API of
8
+ [`ws`](https://github.com/websockets/ws), delivered as a native addon whose framing
9
+ engine is written in Zig. It targets `ws` 8.21.3 and reproduces that release's
10
+ observable behaviour rather than its source. ventiws is not affiliated with the
11
+ `ws` project and vendors none of its code; the upstream API reference is vendored
12
+ as the pinned contract, credited in
13
+ [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
14
+
15
+ **ventiws is at `1.0.0-alpha.11`.** The surface may change between alpha
16
+ releases. What "alpha" means here is bounded: the `ws` compatibility
17
+ contract is tracked row by row in
18
+ [COMPATIBILITY.md](COMPATIBILITY.md), and the four places ventiws knowingly
19
+ differs from `ws` are listed in [CHANGELOG.md](CHANGELOG.md) under "Known
20
+ divergences from `ws`".
21
+
22
+ ## Install
23
+
24
+ ```sh
25
+ pnpm add ventiws
26
+ ```
27
+
28
+ The addon arrives as a per-platform package that npm installs only on a host
29
+ it matches, so the install needs no compiler:
30
+
31
+ | Platform | Package |
32
+ | --------------------------- | ---------------------------------- |
33
+ | Linux x64, glibc | `@ventiws/binding-linux-x64-gnu` |
34
+ | Linux x64, musl (Alpine) | `@ventiws/binding-linux-x64-musl` |
35
+ | Linux arm64, glibc | `@ventiws/binding-linux-arm64-gnu` |
36
+ | macOS x64 | `@ventiws/binding-darwin-x64` |
37
+ | macOS arm64 (Apple silicon) | `@ventiws/binding-darwin-arm64` |
38
+
39
+ Any other host installs ventiws with no addon behind it, and the first
40
+ `WebSocketServer` says so by name and lists the platforms above.
41
+
42
+ **Windows is not supported yet, at either layer.** There is no published addon,
43
+ because the pinned `napi-zig` asks the build for an ABI-less
44
+ `x86_64-windows-none` that has no `ws2_32` to link. More fundamentally, the
45
+ engine cannot serve a socket there: its listener binds and the kernel accepts,
46
+ then the event loop fails `accept` and `read` with `error.Unexpected`, and the
47
+ peer is reset. Building in a checkout does not change that, so a Windows
48
+ checkout is a place to edit the code, not to run a server. The frame codec
49
+ itself is platform-independent and its suites do pass there.
50
+
51
+ Working in the tree:
52
+
53
+ ```sh
54
+ git clone git@github.com:farbenbuilds/ventiws.git
55
+ cd ventiws
56
+ direnv allow
57
+ pnpm install
58
+ pnpm build
59
+ ```
60
+
61
+ `direnv allow` runs `.envrc`, which enters the pinned Nix shell with Node.js,
62
+ pnpm, and Zig 0.16.0, and re-enters it on every later `cd`. Without
63
+ [direnv](https://direnv.net) installed, run `nix develop` once per shell
64
+ instead. The first `pnpm build` compiles the vendored C dependencies, which
65
+ takes minutes and about a gigabyte; later builds are incremental.
66
+
67
+ ## Usage
68
+
69
+ ```ts
70
+ import { WebSocket, WebSocketServer } from "ventiws";
71
+
72
+ const server = new WebSocketServer({ port: 8080 });
73
+
74
+ server.on("listening", () => {
75
+ console.log("listening on", server.address()?.port);
76
+ });
77
+
78
+ server.on("connection", (socket) => {
79
+ socket.on("message", (data, isBinary) => {
80
+ socket.send(isBinary ? data : `echo: ${data.toString()}`);
81
+ });
82
+ socket.on("close", (code, reason) => {
83
+ console.log("closed", code, reason.toString());
84
+ });
85
+ });
86
+
87
+ const client = new WebSocket("ws://127.0.0.1:8080/");
88
+ client.on("open", () => client.send("hello"));
89
+ client.on("message", (data) => console.log("client saw", data.toString()));
90
+ client.on("error", (error) => console.error(error));
91
+ ```
92
+
93
+ Both halves run against a real `ws` peer in this repository's suites, and the
94
+ constructors are plain functions returning state records: no `class`, no `this`,
95
+ no prototype.
96
+
97
+ ## Status
98
+
99
+ Option defaults match `ws`: `maxPayload` 100 MiB, `maxFragments` 16384, and
100
+ `closeTimeout` 30000 ms. The itemised matrix, with the owner module and the
101
+ evidence test behind every row, is [COMPATIBILITY.md](COMPATIBILITY.md).
102
+
103
+ | Area | State |
104
+ | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
105
+ | `WebSocketServer`: options, events, `handleUpgrade`, `shouldHandle`, `verifyClient` | Works, compared property for property against `ws` |
106
+ | Server-side `WebSocket`: properties, `on*` and DOM handlers, ready states | Works, with exactly-once `close` |
107
+ | `WebSocket` client: `net` and `tls`, redirects, `ws+unix:`, `unexpected-response` | Works against a real `ws` server |
108
+ | `send`, fragmentation, `binaryType`, `ping`/`pong`, close codes | Works both directions; `maxPayload` and `maxFragments` are enforced per connection |
109
+ | `permessage-deflate` | Works; context takeover is declined in both directions ([why](COMPATIBILITY.md#shared-with-the-engine)) |
110
+ | `createWebSocketStream`, `clientTracking`, `server.options` | Works, including the `WebSocket` class option |
111
+ | Engine route message cap, 64 KiB | A property of the build. [Two limits](#two-limits-worth-knowing) |
112
+ | Engine route inbound ring, 64 messages | Excess is dropped and counted. Same section |
113
+ | RFC 6455 Autobahn suite | Regression gate; 424 of 517 cases in the full run, 268 in the framing selection ([matrix](COMPATIBILITY.md#rfc-6455-conformance)) |
114
+ | Client `wss:` to `ws:` downgrade, redirect hop limit, `origin`, `handshakeTimeout` | Implemented with no behavioural test ([outstanding](COMPATIBILITY.md#what-is-still-outstanding)) |
115
+
116
+ ## Two limits worth knowing
117
+
118
+ **The 64 KiB message cap.** `message_capacity` in
119
+ `src/engine/server/capacities.zig` is a `comptime` constant, so it is baked into
120
+ the addon: `maxPayload` sizes each connection's own ceiling and does not lift this
121
+ one. 64 KiB is the largest payload the conformance suite puts on the wire, and a
122
+ frame above it is closed with 1009. Raising it costs about 22 MB per live server,
123
+ because the message slab, the write queue, and both staging rings scale with it.
124
+
125
+ **The 64-slot inbound ring.** The engine has no hook for stopping a read when its
126
+ consumer falls behind, so the ring in `src/engine/server/instance.zig` is the
127
+ only place a burst can be absorbed. It holds 64 messages, and a peer delivering
128
+ more before the Node main thread drains has the excess dropped and counted by
129
+ `serverDroppedMessages`. `ws` applies backpressure instead, so it has no cliff.
130
+
131
+ Both bind the engine route, which is µWebZockets' own listener. The codec route
132
+ the public `ws` surface uses is bound by neither: its buffers grow into each
133
+ connection's own `maxPayload` and `maxFragments`. `engineLimits()` is exported
134
+ from the package entry, so the compiled numbers are readable at runtime.
135
+
136
+ ## Commands
137
+
138
+ ```sh
139
+ pnpm build
140
+ pnpm test
141
+ pnpm test:compat
142
+ pnpm test:autobahn
143
+ pnpm bench
144
+ pnpm typecheck
145
+ pnpm lint
146
+ pnpm format:check
147
+ ```
148
+
149
+ ## Docs and licence
150
+
151
+ - [COMPATIBILITY.md](COMPATIBILITY.md): every `ws` surface item, its owner module, its status, and its evidence test.
152
+ - [CODEBASE.md](CODEBASE.md): repository layout, the two routes, the boundary, and the compiled capacities.
153
+ - [CODING_CONVENTION.md](CODING_CONVENTION.md): TypeScript and Zig style, the anti-OOP rules, and the module budget.
154
+ - [docs/migrating.md](docs/migrating.md): moving an existing `ws` application over.
155
+ - [CONTRIBUTE.md](CONTRIBUTE.md): environment setup, the script contract, and how to release.
156
+
157
+ MIT. See [LICENSE](LICENSE). `ws` is copyright Einar Otto Stangvik, Arnout
158
+ Kazemier, and contributors, MIT-licensed. µWebZockets is the first-party
159
+ MIT-licensed protocol engine written for this project. Provenance for everything
160
+ vendored or CI-only is in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ventiws",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.11",
4
4
  "description": "A drop-in replacement for ws for Node.js, built on the first-party µWebZockets Zig engine via napi-zig.",
5
5
  "homepage": "https://github.com/farbenbuilds/ventiws#readme",
6
6
  "bugs": {
@@ -30,10 +30,10 @@
30
30
  "node": ">=22.12.0"
31
31
  },
32
32
  "optionalDependencies": {
33
- "@ventiws/binding-darwin-arm64": "1.0.0-alpha.1",
34
- "@ventiws/binding-darwin-x64": "1.0.0-alpha.1",
35
- "@ventiws/binding-linux-arm64-gnu": "1.0.0-alpha.1",
36
- "@ventiws/binding-linux-x64-gnu": "1.0.0-alpha.1",
37
- "@ventiws/binding-linux-x64-musl": "1.0.0-alpha.1"
33
+ "@ventiws/binding-darwin-arm64": "1.0.0-alpha.11",
34
+ "@ventiws/binding-darwin-x64": "1.0.0-alpha.11",
35
+ "@ventiws/binding-linux-arm64-gnu": "1.0.0-alpha.11",
36
+ "@ventiws/binding-linux-x64-gnu": "1.0.0-alpha.11",
37
+ "@ventiws/binding-linux-x64-musl": "1.0.0-alpha.11"
38
38
  }
39
39
  }