ventiws 1.0.0-rc.1 → 1.0.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.cjs +101 -112
- package/dist/index.d.cts +4 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +101 -112
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
A WebSocket implementation for Node.js with the API of
|
|
8
8
|
[`ws`](https://github.com/websockets/ws), delivered as a native addon whose framing
|
|
9
|
-
engine is written in Zig. It targets `ws` 8.
|
|
9
|
+
engine is written in Zig. It targets `ws` 8.22.0 and reproduces that release's
|
|
10
10
|
observable behaviour rather than its source. ventiws is not affiliated with the
|
|
11
11
|
`ws` project and vendors none of its code; the upstream API reference is vendored
|
|
12
12
|
as the pinned contract, credited in
|
|
13
13
|
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
14
14
|
|
|
15
|
-
**ventiws is at `1.0.0-rc.
|
|
15
|
+
**ventiws is at `1.0.0-rc.3`.** The surface may change between beta
|
|
16
16
|
releases. What "beta" means here is bounded: the `ws` compatibility
|
|
17
17
|
contract is tracked row by row in
|
|
18
18
|
[COMPATIBILITY.md](COMPATIBILITY.md), and the four places ventiws knowingly
|
package/dist/index.cjs
CHANGED
|
@@ -459,6 +459,97 @@ function createEmitter(state) {
|
|
|
459
459
|
}
|
|
460
460
|
};
|
|
461
461
|
}
|
|
462
|
+
function normalizeParameters(parameters, isServer) {
|
|
463
|
+
const out = {};
|
|
464
|
+
for (const [key, values] of Object.entries(parameters)) {
|
|
465
|
+
if (values.length > 1) return null;
|
|
466
|
+
const parsed = parseParameter(key, values[0] ?? "", isServer);
|
|
467
|
+
if (parsed === null) return null;
|
|
468
|
+
out[key] = parsed;
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
server_no_context_takeover: out.server_no_context_takeover === true,
|
|
472
|
+
client_no_context_takeover: out.client_no_context_takeover === true,
|
|
473
|
+
server_max_window_bits: numberOf(out.server_max_window_bits),
|
|
474
|
+
client_max_window_bits: windowOf(out.client_max_window_bits)
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function parseParameter(key, value, isServer) {
|
|
478
|
+
if (key === "server_no_context_takeover" || key === "client_no_context_takeover") return value === "" ? true : null;
|
|
479
|
+
if (key === "client_max_window_bits") {
|
|
480
|
+
if (value === "") return isServer ? true : null;
|
|
481
|
+
return windowSize(value);
|
|
482
|
+
}
|
|
483
|
+
if (key === "server_max_window_bits") return value === "" ? null : windowSize(value);
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
function windowSize(value) {
|
|
487
|
+
const parsed = Number(value);
|
|
488
|
+
if (!Number.isInteger(parsed) || parsed < 8 || parsed > 15) return null;
|
|
489
|
+
return parsed;
|
|
490
|
+
}
|
|
491
|
+
function windowOf(value) {
|
|
492
|
+
return typeof value === "number" || value === true ? value : void 0;
|
|
493
|
+
}
|
|
494
|
+
function numberOf(value) {
|
|
495
|
+
return typeof value === "number" ? value : void 0;
|
|
496
|
+
}
|
|
497
|
+
//#endregion
|
|
498
|
+
//#region src/compat/options/shared.ts
|
|
499
|
+
const DEFAULT_MAX_PAYLOAD = 104857600;
|
|
500
|
+
const DEFAULT_MAX_FRAGMENTS = 16384;
|
|
501
|
+
const DEFAULT_MAX_BUFFERED_CHUNKS = 262144;
|
|
502
|
+
const DEFAULT_CLOSE_TIMEOUT = 3e4;
|
|
503
|
+
const SUBPROTOCOL_PATTERN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
504
|
+
function isProtocolToken(value) {
|
|
505
|
+
return typeof value === "string" && SUBPROTOCOL_PATTERN.test(value);
|
|
506
|
+
}
|
|
507
|
+
function invalidOption(message, constructor = TypeError) {
|
|
508
|
+
throw createError("ERR_INVALID_OPTION", message, constructor);
|
|
509
|
+
}
|
|
510
|
+
function normalizeProtocols(protocols) {
|
|
511
|
+
if (protocols === void 0) return [];
|
|
512
|
+
const list = typeof protocols === "string" ? [protocols] : protocols;
|
|
513
|
+
const candidates = Array.isArray(list) ? list : [list];
|
|
514
|
+
const seen = /* @__PURE__ */ new Set();
|
|
515
|
+
const result = [];
|
|
516
|
+
for (const protocol of candidates) {
|
|
517
|
+
if (typeof protocol !== "string" || !SUBPROTOCOL_PATTERN.test(protocol) || seen.has(protocol)) invalidOption("An invalid or duplicated subprotocol was specified", SyntaxError);
|
|
518
|
+
seen.add(protocol);
|
|
519
|
+
result.push(protocol);
|
|
520
|
+
}
|
|
521
|
+
return result;
|
|
522
|
+
}
|
|
523
|
+
function parseProtocolHeader(header) {
|
|
524
|
+
return normalizeProtocols(header.split(",").map((protocol) => protocol.trim()));
|
|
525
|
+
}
|
|
526
|
+
function windowBitsOf(name, raw) {
|
|
527
|
+
if (raw === void 0 || raw === false) return raw;
|
|
528
|
+
if (typeof raw === "number" && Number.isInteger(raw) && raw >= 8 && raw <= 15) return raw;
|
|
529
|
+
return invalidOption(`The ${name} option must be an integer in [8, 15] (received ${String(raw)})`, RangeError);
|
|
530
|
+
}
|
|
531
|
+
function normalizePerMessageDeflate(value, fallback) {
|
|
532
|
+
if (value !== void 0 && !value) return false;
|
|
533
|
+
const resolved = value ?? fallback;
|
|
534
|
+
if (!resolved) return false;
|
|
535
|
+
const options = resolved === true ? {} : resolved;
|
|
536
|
+
return {
|
|
537
|
+
serverNoContextTakeover: options.serverNoContextTakeover,
|
|
538
|
+
clientNoContextTakeover: options.clientNoContextTakeover,
|
|
539
|
+
serverMaxWindowBits: windowBitsOf("serverMaxWindowBits", options.serverMaxWindowBits),
|
|
540
|
+
clientMaxWindowBits: windowBitsOf("clientMaxWindowBits", options.clientMaxWindowBits),
|
|
541
|
+
threshold: options.threshold ?? 1024,
|
|
542
|
+
concurrencyLimit: options.concurrencyLimit ?? 10,
|
|
543
|
+
zlibDeflateOptions: options.zlibDeflateOptions,
|
|
544
|
+
zlibInflateOptions: options.zlibInflateOptions
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function closeTimeoutOf(source) {
|
|
548
|
+
const raw = source.closeTimeout;
|
|
549
|
+
if (raw === void 0) return DEFAULT_CLOSE_TIMEOUT;
|
|
550
|
+
if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 0) invalidOption(`The closeTimeout option must be a non-negative number (received ${String(raw)})`, RangeError);
|
|
551
|
+
return raw;
|
|
552
|
+
}
|
|
462
553
|
//#endregion
|
|
463
554
|
//#region src/compat/server/clients.ts
|
|
464
555
|
function clientCount(state) {
|
|
@@ -475,7 +566,7 @@ function trackClient(state, socket) {
|
|
|
475
566
|
}
|
|
476
567
|
//#endregion
|
|
477
568
|
//#region package.json
|
|
478
|
-
var version = "1.0.0-rc.
|
|
569
|
+
var version = "1.0.0-rc.3";
|
|
479
570
|
//#endregion
|
|
480
571
|
//#region src/logging/lifecycle.ts
|
|
481
572
|
const CONTROL = /\p{Cc}/gu;
|
|
@@ -557,97 +648,6 @@ function closeWebSocketServer(state, callback) {
|
|
|
557
648
|
emitClose(state);
|
|
558
649
|
});
|
|
559
650
|
}
|
|
560
|
-
function normalizeParameters(parameters, isServer) {
|
|
561
|
-
const out = {};
|
|
562
|
-
for (const [key, values] of Object.entries(parameters)) {
|
|
563
|
-
if (values.length > 1) return null;
|
|
564
|
-
const parsed = parseParameter(key, values[0] ?? "", isServer);
|
|
565
|
-
if (parsed === null) return null;
|
|
566
|
-
out[key] = parsed;
|
|
567
|
-
}
|
|
568
|
-
return {
|
|
569
|
-
server_no_context_takeover: out.server_no_context_takeover === true,
|
|
570
|
-
client_no_context_takeover: out.client_no_context_takeover === true,
|
|
571
|
-
server_max_window_bits: numberOf(out.server_max_window_bits),
|
|
572
|
-
client_max_window_bits: windowOf(out.client_max_window_bits)
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
function parseParameter(key, value, isServer) {
|
|
576
|
-
if (key === "server_no_context_takeover" || key === "client_no_context_takeover") return value === "" ? true : null;
|
|
577
|
-
if (key === "client_max_window_bits") {
|
|
578
|
-
if (value === "") return isServer ? true : null;
|
|
579
|
-
return windowSize(value);
|
|
580
|
-
}
|
|
581
|
-
if (key === "server_max_window_bits") return value === "" ? null : windowSize(value);
|
|
582
|
-
return null;
|
|
583
|
-
}
|
|
584
|
-
function windowSize(value) {
|
|
585
|
-
const parsed = Number(value);
|
|
586
|
-
if (!Number.isInteger(parsed) || parsed < 8 || parsed > 15) return null;
|
|
587
|
-
return parsed;
|
|
588
|
-
}
|
|
589
|
-
function windowOf(value) {
|
|
590
|
-
return typeof value === "number" || value === true ? value : void 0;
|
|
591
|
-
}
|
|
592
|
-
function numberOf(value) {
|
|
593
|
-
return typeof value === "number" ? value : void 0;
|
|
594
|
-
}
|
|
595
|
-
//#endregion
|
|
596
|
-
//#region src/compat/options/shared.ts
|
|
597
|
-
const DEFAULT_MAX_PAYLOAD = 104857600;
|
|
598
|
-
const DEFAULT_MAX_FRAGMENTS = 16384;
|
|
599
|
-
const DEFAULT_MAX_BUFFERED_CHUNKS = 262144;
|
|
600
|
-
const DEFAULT_CLOSE_TIMEOUT = 3e4;
|
|
601
|
-
const SUBPROTOCOL_PATTERN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
602
|
-
function isProtocolToken(value) {
|
|
603
|
-
return typeof value === "string" && SUBPROTOCOL_PATTERN.test(value);
|
|
604
|
-
}
|
|
605
|
-
function invalidOption(message, constructor = TypeError) {
|
|
606
|
-
throw createError("ERR_INVALID_OPTION", message, constructor);
|
|
607
|
-
}
|
|
608
|
-
function normalizeProtocols$1(protocols) {
|
|
609
|
-
if (protocols === void 0) return [];
|
|
610
|
-
const list = typeof protocols === "string" ? [protocols] : protocols;
|
|
611
|
-
const candidates = Array.isArray(list) ? list : [list];
|
|
612
|
-
const seen = /* @__PURE__ */ new Set();
|
|
613
|
-
const result = [];
|
|
614
|
-
for (const protocol of candidates) {
|
|
615
|
-
if (typeof protocol !== "string" || !SUBPROTOCOL_PATTERN.test(protocol) || seen.has(protocol)) invalidOption("An invalid or duplicated subprotocol was specified", SyntaxError);
|
|
616
|
-
seen.add(protocol);
|
|
617
|
-
result.push(protocol);
|
|
618
|
-
}
|
|
619
|
-
return result;
|
|
620
|
-
}
|
|
621
|
-
function parseProtocolHeader(header) {
|
|
622
|
-
return normalizeProtocols$1(header.split(",").map((protocol) => protocol.trim()));
|
|
623
|
-
}
|
|
624
|
-
function windowBitsOf(name, raw) {
|
|
625
|
-
if (raw === void 0 || raw === false) return raw;
|
|
626
|
-
if (typeof raw === "number" && Number.isInteger(raw) && raw >= 8 && raw <= 15) return raw;
|
|
627
|
-
return invalidOption(`The ${name} option must be an integer in [8, 15] (received ${String(raw)})`, RangeError);
|
|
628
|
-
}
|
|
629
|
-
function normalizePerMessageDeflate(value, fallback) {
|
|
630
|
-
if (value !== void 0 && !value) return false;
|
|
631
|
-
const resolved = value ?? fallback;
|
|
632
|
-
if (!resolved) return false;
|
|
633
|
-
const options = resolved === true ? {} : resolved;
|
|
634
|
-
return {
|
|
635
|
-
serverNoContextTakeover: options.serverNoContextTakeover,
|
|
636
|
-
clientNoContextTakeover: options.clientNoContextTakeover,
|
|
637
|
-
serverMaxWindowBits: windowBitsOf("serverMaxWindowBits", options.serverMaxWindowBits),
|
|
638
|
-
clientMaxWindowBits: windowBitsOf("clientMaxWindowBits", options.clientMaxWindowBits),
|
|
639
|
-
threshold: options.threshold ?? 1024,
|
|
640
|
-
concurrencyLimit: options.concurrencyLimit ?? 10,
|
|
641
|
-
zlibDeflateOptions: options.zlibDeflateOptions,
|
|
642
|
-
zlibInflateOptions: options.zlibInflateOptions
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
function closeTimeoutOf(source) {
|
|
646
|
-
const raw = source.closeTimeout;
|
|
647
|
-
if (raw === void 0) return DEFAULT_CLOSE_TIMEOUT;
|
|
648
|
-
if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 0) invalidOption(`The closeTimeout option must be a non-negative number (received ${String(raw)})`, RangeError);
|
|
649
|
-
return raw;
|
|
650
|
-
}
|
|
651
651
|
//#endregion
|
|
652
652
|
//#region src/compat/server/handshake.ts
|
|
653
653
|
const KEY_PATTERN = /^[+/0-9A-Za-z]{22}==$/;
|
|
@@ -1389,9 +1389,9 @@ function closeConnection(state, code, reason) {
|
|
|
1389
1389
|
return;
|
|
1390
1390
|
}
|
|
1391
1391
|
if (state.readyState === 2) return;
|
|
1392
|
-
state.readyState = 2;
|
|
1393
1392
|
const closeCode = closeCodeOf(code);
|
|
1394
1393
|
const closeReason = toCloseReason(reason);
|
|
1394
|
+
state.readyState = 2;
|
|
1395
1395
|
if (state.codec !== null) {
|
|
1396
1396
|
closeFramed(state, closeCode, closeReason);
|
|
1397
1397
|
armCloseTimeout(state, state.closeTimeout);
|
|
@@ -1903,7 +1903,7 @@ function answer(options, offer) {
|
|
|
1903
1903
|
}
|
|
1904
1904
|
//#endregion
|
|
1905
1905
|
//#region src/compat/extensions/scan.ts
|
|
1906
|
-
const TOKEN
|
|
1906
|
+
const TOKEN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
1907
1907
|
const COMMA$1 = 44;
|
|
1908
1908
|
const SEMICOLON = 59;
|
|
1909
1909
|
const EQUALS = 61;
|
|
@@ -1953,7 +1953,7 @@ function readValue(header, start) {
|
|
|
1953
1953
|
if (header.charCodeAt(start) !== QUOTE) {
|
|
1954
1954
|
const token = readToken(header, start, "a parameter value");
|
|
1955
1955
|
if (token === null) throw unexpectedEnd(header, start);
|
|
1956
|
-
if (!TOKEN
|
|
1956
|
+
if (!TOKEN.test(token)) throw syntax(header, start, "invalid parameter value");
|
|
1957
1957
|
return {
|
|
1958
1958
|
value: token,
|
|
1959
1959
|
end: start + token.length
|
|
@@ -2280,10 +2280,10 @@ function createWebSocketServer(socketClass, options, callback) {
|
|
|
2280
2280
|
const resolved = {
|
|
2281
2281
|
allowSynchronousEvents: true,
|
|
2282
2282
|
autoPong: true,
|
|
2283
|
-
maxBufferedChunks:
|
|
2284
|
-
maxFragments:
|
|
2285
|
-
closeTimeout:
|
|
2286
|
-
maxPayload:
|
|
2283
|
+
maxBufferedChunks: DEFAULT_MAX_BUFFERED_CHUNKS,
|
|
2284
|
+
maxFragments: DEFAULT_MAX_FRAGMENTS,
|
|
2285
|
+
closeTimeout: DEFAULT_CLOSE_TIMEOUT,
|
|
2286
|
+
maxPayload: DEFAULT_MAX_PAYLOAD,
|
|
2287
2287
|
skipUTF8Validation: false,
|
|
2288
2288
|
perMessageDeflate: false,
|
|
2289
2289
|
handleProtocols: null,
|
|
@@ -2350,18 +2350,6 @@ function createWebSocketServer(socketClass, options, callback) {
|
|
|
2350
2350
|
}
|
|
2351
2351
|
//#endregion
|
|
2352
2352
|
//#region src/compat/client/protocols.ts
|
|
2353
|
-
const TOKEN = /^[!#$%&'*+\-.^_`|~\dA-Za-z]+$/;
|
|
2354
|
-
function normalizeProtocols(protocols) {
|
|
2355
|
-
if (protocols === void 0) return [];
|
|
2356
|
-
const list = typeof protocols === "string" ? [protocols] : protocols;
|
|
2357
|
-
if (!Array.isArray(list)) throw createError("ERR_INVALID_OPTION", "An invalid or duplicated subprotocol was specified", SyntaxError);
|
|
2358
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2359
|
-
for (const protocol of list) {
|
|
2360
|
-
if (typeof protocol !== "string" || !TOKEN.test(protocol) || seen.has(protocol)) throw createError("ERR_INVALID_OPTION", "An invalid or duplicated subprotocol was specified", SyntaxError);
|
|
2361
|
-
seen.add(protocol);
|
|
2362
|
-
}
|
|
2363
|
-
return [...list];
|
|
2364
|
-
}
|
|
2365
2353
|
function protocolSet(protocols) {
|
|
2366
2354
|
return new Set(protocols);
|
|
2367
2355
|
}
|
|
@@ -2814,6 +2802,7 @@ function normalizeClientOptions(options) {
|
|
|
2814
2802
|
skipUTF8Validation: source.skipUTF8Validation ?? false,
|
|
2815
2803
|
allowSynchronousEvents: source.allowSynchronousEvents ?? true,
|
|
2816
2804
|
autoPong: source.autoPong ?? true,
|
|
2805
|
+
protocols: source.protocols,
|
|
2817
2806
|
closeTimeout: closeTimeoutOf(source),
|
|
2818
2807
|
perMessageDeflate: normalizePerMessageDeflate(source.perMessageDeflate, true),
|
|
2819
2808
|
origin: source.origin,
|
|
@@ -3211,7 +3200,7 @@ function dial(attempt) {
|
|
|
3211
3200
|
function connectSocket(address, protocols, options) {
|
|
3212
3201
|
const normalized = normalizeClientOptions(options);
|
|
3213
3202
|
const parsed = parseAddress(address);
|
|
3214
|
-
const requested = normalizeProtocols(protocols);
|
|
3203
|
+
const requested = normalizeProtocols(protocols === void 0 ? normalized.protocols : protocols);
|
|
3215
3204
|
const state = createSocketState();
|
|
3216
3205
|
state.isServer = false;
|
|
3217
3206
|
state.url = parsed.url;
|
package/dist/index.d.cts
CHANGED
|
@@ -202,6 +202,8 @@ declare namespace WebSocket$1 {
|
|
|
202
202
|
allowSynchronousEvents?: boolean | undefined;
|
|
203
203
|
autoPong?: boolean | undefined;
|
|
204
204
|
maxPayload?: number | undefined;
|
|
205
|
+
maxBufferedChunks?: number | undefined;
|
|
206
|
+
maxFragments?: number | undefined;
|
|
205
207
|
skipUTF8Validation?: boolean | undefined;
|
|
206
208
|
createConnection?: typeof createConnection | undefined;
|
|
207
209
|
finishRequest?: FinishRequestCallback | undefined;
|
|
@@ -271,6 +273,8 @@ declare namespace WebSocket$1 {
|
|
|
271
273
|
clientTracking?: boolean | undefined;
|
|
272
274
|
perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined;
|
|
273
275
|
maxPayload?: number | undefined;
|
|
276
|
+
maxBufferedChunks?: number | undefined;
|
|
277
|
+
maxFragments?: number | undefined;
|
|
274
278
|
skipUTF8Validation?: boolean | undefined;
|
|
275
279
|
WebSocket?: U | undefined;
|
|
276
280
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -203,6 +203,8 @@ declare namespace WebSocket$1 {
|
|
|
203
203
|
allowSynchronousEvents?: boolean | undefined;
|
|
204
204
|
autoPong?: boolean | undefined;
|
|
205
205
|
maxPayload?: number | undefined;
|
|
206
|
+
maxBufferedChunks?: number | undefined;
|
|
207
|
+
maxFragments?: number | undefined;
|
|
206
208
|
skipUTF8Validation?: boolean | undefined;
|
|
207
209
|
createConnection?: typeof createConnection | undefined;
|
|
208
210
|
finishRequest?: FinishRequestCallback | undefined;
|
|
@@ -272,6 +274,8 @@ declare namespace WebSocket$1 {
|
|
|
272
274
|
clientTracking?: boolean | undefined;
|
|
273
275
|
perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined;
|
|
274
276
|
maxPayload?: number | undefined;
|
|
277
|
+
maxBufferedChunks?: number | undefined;
|
|
278
|
+
maxFragments?: number | undefined;
|
|
275
279
|
skipUTF8Validation?: boolean | undefined;
|
|
276
280
|
WebSocket?: U | undefined;
|
|
277
281
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -432,6 +432,97 @@ function createEmitter(state) {
|
|
|
432
432
|
}
|
|
433
433
|
};
|
|
434
434
|
}
|
|
435
|
+
function normalizeParameters(parameters, isServer) {
|
|
436
|
+
const out = {};
|
|
437
|
+
for (const [key, values] of Object.entries(parameters)) {
|
|
438
|
+
if (values.length > 1) return null;
|
|
439
|
+
const parsed = parseParameter(key, values[0] ?? "", isServer);
|
|
440
|
+
if (parsed === null) return null;
|
|
441
|
+
out[key] = parsed;
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
server_no_context_takeover: out.server_no_context_takeover === true,
|
|
445
|
+
client_no_context_takeover: out.client_no_context_takeover === true,
|
|
446
|
+
server_max_window_bits: numberOf(out.server_max_window_bits),
|
|
447
|
+
client_max_window_bits: windowOf(out.client_max_window_bits)
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function parseParameter(key, value, isServer) {
|
|
451
|
+
if (key === "server_no_context_takeover" || key === "client_no_context_takeover") return value === "" ? true : null;
|
|
452
|
+
if (key === "client_max_window_bits") {
|
|
453
|
+
if (value === "") return isServer ? true : null;
|
|
454
|
+
return windowSize(value);
|
|
455
|
+
}
|
|
456
|
+
if (key === "server_max_window_bits") return value === "" ? null : windowSize(value);
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
function windowSize(value) {
|
|
460
|
+
const parsed = Number(value);
|
|
461
|
+
if (!Number.isInteger(parsed) || parsed < 8 || parsed > 15) return null;
|
|
462
|
+
return parsed;
|
|
463
|
+
}
|
|
464
|
+
function windowOf(value) {
|
|
465
|
+
return typeof value === "number" || value === true ? value : void 0;
|
|
466
|
+
}
|
|
467
|
+
function numberOf(value) {
|
|
468
|
+
return typeof value === "number" ? value : void 0;
|
|
469
|
+
}
|
|
470
|
+
//#endregion
|
|
471
|
+
//#region src/compat/options/shared.ts
|
|
472
|
+
const DEFAULT_MAX_PAYLOAD = 104857600;
|
|
473
|
+
const DEFAULT_MAX_FRAGMENTS = 16384;
|
|
474
|
+
const DEFAULT_MAX_BUFFERED_CHUNKS = 262144;
|
|
475
|
+
const DEFAULT_CLOSE_TIMEOUT = 3e4;
|
|
476
|
+
const SUBPROTOCOL_PATTERN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
477
|
+
function isProtocolToken(value) {
|
|
478
|
+
return typeof value === "string" && SUBPROTOCOL_PATTERN.test(value);
|
|
479
|
+
}
|
|
480
|
+
function invalidOption(message, constructor = TypeError) {
|
|
481
|
+
throw createError("ERR_INVALID_OPTION", message, constructor);
|
|
482
|
+
}
|
|
483
|
+
function normalizeProtocols(protocols) {
|
|
484
|
+
if (protocols === void 0) return [];
|
|
485
|
+
const list = typeof protocols === "string" ? [protocols] : protocols;
|
|
486
|
+
const candidates = Array.isArray(list) ? list : [list];
|
|
487
|
+
const seen = /* @__PURE__ */ new Set();
|
|
488
|
+
const result = [];
|
|
489
|
+
for (const protocol of candidates) {
|
|
490
|
+
if (typeof protocol !== "string" || !SUBPROTOCOL_PATTERN.test(protocol) || seen.has(protocol)) invalidOption("An invalid or duplicated subprotocol was specified", SyntaxError);
|
|
491
|
+
seen.add(protocol);
|
|
492
|
+
result.push(protocol);
|
|
493
|
+
}
|
|
494
|
+
return result;
|
|
495
|
+
}
|
|
496
|
+
function parseProtocolHeader(header) {
|
|
497
|
+
return normalizeProtocols(header.split(",").map((protocol) => protocol.trim()));
|
|
498
|
+
}
|
|
499
|
+
function windowBitsOf(name, raw) {
|
|
500
|
+
if (raw === void 0 || raw === false) return raw;
|
|
501
|
+
if (typeof raw === "number" && Number.isInteger(raw) && raw >= 8 && raw <= 15) return raw;
|
|
502
|
+
return invalidOption(`The ${name} option must be an integer in [8, 15] (received ${String(raw)})`, RangeError);
|
|
503
|
+
}
|
|
504
|
+
function normalizePerMessageDeflate(value, fallback) {
|
|
505
|
+
if (value !== void 0 && !value) return false;
|
|
506
|
+
const resolved = value ?? fallback;
|
|
507
|
+
if (!resolved) return false;
|
|
508
|
+
const options = resolved === true ? {} : resolved;
|
|
509
|
+
return {
|
|
510
|
+
serverNoContextTakeover: options.serverNoContextTakeover,
|
|
511
|
+
clientNoContextTakeover: options.clientNoContextTakeover,
|
|
512
|
+
serverMaxWindowBits: windowBitsOf("serverMaxWindowBits", options.serverMaxWindowBits),
|
|
513
|
+
clientMaxWindowBits: windowBitsOf("clientMaxWindowBits", options.clientMaxWindowBits),
|
|
514
|
+
threshold: options.threshold ?? 1024,
|
|
515
|
+
concurrencyLimit: options.concurrencyLimit ?? 10,
|
|
516
|
+
zlibDeflateOptions: options.zlibDeflateOptions,
|
|
517
|
+
zlibInflateOptions: options.zlibInflateOptions
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
function closeTimeoutOf(source) {
|
|
521
|
+
const raw = source.closeTimeout;
|
|
522
|
+
if (raw === void 0) return DEFAULT_CLOSE_TIMEOUT;
|
|
523
|
+
if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 0) invalidOption(`The closeTimeout option must be a non-negative number (received ${String(raw)})`, RangeError);
|
|
524
|
+
return raw;
|
|
525
|
+
}
|
|
435
526
|
//#endregion
|
|
436
527
|
//#region src/compat/server/clients.ts
|
|
437
528
|
function clientCount(state) {
|
|
@@ -448,7 +539,7 @@ function trackClient(state, socket) {
|
|
|
448
539
|
}
|
|
449
540
|
//#endregion
|
|
450
541
|
//#region package.json
|
|
451
|
-
var version = "1.0.0-rc.
|
|
542
|
+
var version = "1.0.0-rc.3";
|
|
452
543
|
//#endregion
|
|
453
544
|
//#region src/logging/lifecycle.ts
|
|
454
545
|
const CONTROL = /\p{Cc}/gu;
|
|
@@ -530,97 +621,6 @@ function closeWebSocketServer(state, callback) {
|
|
|
530
621
|
emitClose(state);
|
|
531
622
|
});
|
|
532
623
|
}
|
|
533
|
-
function normalizeParameters(parameters, isServer) {
|
|
534
|
-
const out = {};
|
|
535
|
-
for (const [key, values] of Object.entries(parameters)) {
|
|
536
|
-
if (values.length > 1) return null;
|
|
537
|
-
const parsed = parseParameter(key, values[0] ?? "", isServer);
|
|
538
|
-
if (parsed === null) return null;
|
|
539
|
-
out[key] = parsed;
|
|
540
|
-
}
|
|
541
|
-
return {
|
|
542
|
-
server_no_context_takeover: out.server_no_context_takeover === true,
|
|
543
|
-
client_no_context_takeover: out.client_no_context_takeover === true,
|
|
544
|
-
server_max_window_bits: numberOf(out.server_max_window_bits),
|
|
545
|
-
client_max_window_bits: windowOf(out.client_max_window_bits)
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
function parseParameter(key, value, isServer) {
|
|
549
|
-
if (key === "server_no_context_takeover" || key === "client_no_context_takeover") return value === "" ? true : null;
|
|
550
|
-
if (key === "client_max_window_bits") {
|
|
551
|
-
if (value === "") return isServer ? true : null;
|
|
552
|
-
return windowSize(value);
|
|
553
|
-
}
|
|
554
|
-
if (key === "server_max_window_bits") return value === "" ? null : windowSize(value);
|
|
555
|
-
return null;
|
|
556
|
-
}
|
|
557
|
-
function windowSize(value) {
|
|
558
|
-
const parsed = Number(value);
|
|
559
|
-
if (!Number.isInteger(parsed) || parsed < 8 || parsed > 15) return null;
|
|
560
|
-
return parsed;
|
|
561
|
-
}
|
|
562
|
-
function windowOf(value) {
|
|
563
|
-
return typeof value === "number" || value === true ? value : void 0;
|
|
564
|
-
}
|
|
565
|
-
function numberOf(value) {
|
|
566
|
-
return typeof value === "number" ? value : void 0;
|
|
567
|
-
}
|
|
568
|
-
//#endregion
|
|
569
|
-
//#region src/compat/options/shared.ts
|
|
570
|
-
const DEFAULT_MAX_PAYLOAD = 104857600;
|
|
571
|
-
const DEFAULT_MAX_FRAGMENTS = 16384;
|
|
572
|
-
const DEFAULT_MAX_BUFFERED_CHUNKS = 262144;
|
|
573
|
-
const DEFAULT_CLOSE_TIMEOUT = 3e4;
|
|
574
|
-
const SUBPROTOCOL_PATTERN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
575
|
-
function isProtocolToken(value) {
|
|
576
|
-
return typeof value === "string" && SUBPROTOCOL_PATTERN.test(value);
|
|
577
|
-
}
|
|
578
|
-
function invalidOption(message, constructor = TypeError) {
|
|
579
|
-
throw createError("ERR_INVALID_OPTION", message, constructor);
|
|
580
|
-
}
|
|
581
|
-
function normalizeProtocols$1(protocols) {
|
|
582
|
-
if (protocols === void 0) return [];
|
|
583
|
-
const list = typeof protocols === "string" ? [protocols] : protocols;
|
|
584
|
-
const candidates = Array.isArray(list) ? list : [list];
|
|
585
|
-
const seen = /* @__PURE__ */ new Set();
|
|
586
|
-
const result = [];
|
|
587
|
-
for (const protocol of candidates) {
|
|
588
|
-
if (typeof protocol !== "string" || !SUBPROTOCOL_PATTERN.test(protocol) || seen.has(protocol)) invalidOption("An invalid or duplicated subprotocol was specified", SyntaxError);
|
|
589
|
-
seen.add(protocol);
|
|
590
|
-
result.push(protocol);
|
|
591
|
-
}
|
|
592
|
-
return result;
|
|
593
|
-
}
|
|
594
|
-
function parseProtocolHeader(header) {
|
|
595
|
-
return normalizeProtocols$1(header.split(",").map((protocol) => protocol.trim()));
|
|
596
|
-
}
|
|
597
|
-
function windowBitsOf(name, raw) {
|
|
598
|
-
if (raw === void 0 || raw === false) return raw;
|
|
599
|
-
if (typeof raw === "number" && Number.isInteger(raw) && raw >= 8 && raw <= 15) return raw;
|
|
600
|
-
return invalidOption(`The ${name} option must be an integer in [8, 15] (received ${String(raw)})`, RangeError);
|
|
601
|
-
}
|
|
602
|
-
function normalizePerMessageDeflate(value, fallback) {
|
|
603
|
-
if (value !== void 0 && !value) return false;
|
|
604
|
-
const resolved = value ?? fallback;
|
|
605
|
-
if (!resolved) return false;
|
|
606
|
-
const options = resolved === true ? {} : resolved;
|
|
607
|
-
return {
|
|
608
|
-
serverNoContextTakeover: options.serverNoContextTakeover,
|
|
609
|
-
clientNoContextTakeover: options.clientNoContextTakeover,
|
|
610
|
-
serverMaxWindowBits: windowBitsOf("serverMaxWindowBits", options.serverMaxWindowBits),
|
|
611
|
-
clientMaxWindowBits: windowBitsOf("clientMaxWindowBits", options.clientMaxWindowBits),
|
|
612
|
-
threshold: options.threshold ?? 1024,
|
|
613
|
-
concurrencyLimit: options.concurrencyLimit ?? 10,
|
|
614
|
-
zlibDeflateOptions: options.zlibDeflateOptions,
|
|
615
|
-
zlibInflateOptions: options.zlibInflateOptions
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
function closeTimeoutOf(source) {
|
|
619
|
-
const raw = source.closeTimeout;
|
|
620
|
-
if (raw === void 0) return DEFAULT_CLOSE_TIMEOUT;
|
|
621
|
-
if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 0) invalidOption(`The closeTimeout option must be a non-negative number (received ${String(raw)})`, RangeError);
|
|
622
|
-
return raw;
|
|
623
|
-
}
|
|
624
624
|
//#endregion
|
|
625
625
|
//#region src/compat/server/handshake.ts
|
|
626
626
|
const KEY_PATTERN = /^[+/0-9A-Za-z]{22}==$/;
|
|
@@ -1362,9 +1362,9 @@ function closeConnection(state, code, reason) {
|
|
|
1362
1362
|
return;
|
|
1363
1363
|
}
|
|
1364
1364
|
if (state.readyState === 2) return;
|
|
1365
|
-
state.readyState = 2;
|
|
1366
1365
|
const closeCode = closeCodeOf(code);
|
|
1367
1366
|
const closeReason = toCloseReason(reason);
|
|
1367
|
+
state.readyState = 2;
|
|
1368
1368
|
if (state.codec !== null) {
|
|
1369
1369
|
closeFramed(state, closeCode, closeReason);
|
|
1370
1370
|
armCloseTimeout(state, state.closeTimeout);
|
|
@@ -1876,7 +1876,7 @@ function answer(options, offer) {
|
|
|
1876
1876
|
}
|
|
1877
1877
|
//#endregion
|
|
1878
1878
|
//#region src/compat/extensions/scan.ts
|
|
1879
|
-
const TOKEN
|
|
1879
|
+
const TOKEN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
1880
1880
|
const COMMA$1 = 44;
|
|
1881
1881
|
const SEMICOLON = 59;
|
|
1882
1882
|
const EQUALS = 61;
|
|
@@ -1926,7 +1926,7 @@ function readValue(header, start) {
|
|
|
1926
1926
|
if (header.charCodeAt(start) !== QUOTE) {
|
|
1927
1927
|
const token = readToken(header, start, "a parameter value");
|
|
1928
1928
|
if (token === null) throw unexpectedEnd(header, start);
|
|
1929
|
-
if (!TOKEN
|
|
1929
|
+
if (!TOKEN.test(token)) throw syntax(header, start, "invalid parameter value");
|
|
1930
1930
|
return {
|
|
1931
1931
|
value: token,
|
|
1932
1932
|
end: start + token.length
|
|
@@ -2253,10 +2253,10 @@ function createWebSocketServer(socketClass, options, callback) {
|
|
|
2253
2253
|
const resolved = {
|
|
2254
2254
|
allowSynchronousEvents: true,
|
|
2255
2255
|
autoPong: true,
|
|
2256
|
-
maxBufferedChunks:
|
|
2257
|
-
maxFragments:
|
|
2258
|
-
closeTimeout:
|
|
2259
|
-
maxPayload:
|
|
2256
|
+
maxBufferedChunks: DEFAULT_MAX_BUFFERED_CHUNKS,
|
|
2257
|
+
maxFragments: DEFAULT_MAX_FRAGMENTS,
|
|
2258
|
+
closeTimeout: DEFAULT_CLOSE_TIMEOUT,
|
|
2259
|
+
maxPayload: DEFAULT_MAX_PAYLOAD,
|
|
2260
2260
|
skipUTF8Validation: false,
|
|
2261
2261
|
perMessageDeflate: false,
|
|
2262
2262
|
handleProtocols: null,
|
|
@@ -2323,18 +2323,6 @@ function createWebSocketServer(socketClass, options, callback) {
|
|
|
2323
2323
|
}
|
|
2324
2324
|
//#endregion
|
|
2325
2325
|
//#region src/compat/client/protocols.ts
|
|
2326
|
-
const TOKEN = /^[!#$%&'*+\-.^_`|~\dA-Za-z]+$/;
|
|
2327
|
-
function normalizeProtocols(protocols) {
|
|
2328
|
-
if (protocols === void 0) return [];
|
|
2329
|
-
const list = typeof protocols === "string" ? [protocols] : protocols;
|
|
2330
|
-
if (!Array.isArray(list)) throw createError("ERR_INVALID_OPTION", "An invalid or duplicated subprotocol was specified", SyntaxError);
|
|
2331
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2332
|
-
for (const protocol of list) {
|
|
2333
|
-
if (typeof protocol !== "string" || !TOKEN.test(protocol) || seen.has(protocol)) throw createError("ERR_INVALID_OPTION", "An invalid or duplicated subprotocol was specified", SyntaxError);
|
|
2334
|
-
seen.add(protocol);
|
|
2335
|
-
}
|
|
2336
|
-
return [...list];
|
|
2337
|
-
}
|
|
2338
2326
|
function protocolSet(protocols) {
|
|
2339
2327
|
return new Set(protocols);
|
|
2340
2328
|
}
|
|
@@ -2787,6 +2775,7 @@ function normalizeClientOptions(options) {
|
|
|
2787
2775
|
skipUTF8Validation: source.skipUTF8Validation ?? false,
|
|
2788
2776
|
allowSynchronousEvents: source.allowSynchronousEvents ?? true,
|
|
2789
2777
|
autoPong: source.autoPong ?? true,
|
|
2778
|
+
protocols: source.protocols,
|
|
2790
2779
|
closeTimeout: closeTimeoutOf(source),
|
|
2791
2780
|
perMessageDeflate: normalizePerMessageDeflate(source.perMessageDeflate, true),
|
|
2792
2781
|
origin: source.origin,
|
|
@@ -3184,7 +3173,7 @@ function dial(attempt) {
|
|
|
3184
3173
|
function connectSocket(address, protocols, options) {
|
|
3185
3174
|
const normalized = normalizeClientOptions(options);
|
|
3186
3175
|
const parsed = parseAddress(address);
|
|
3187
|
-
const requested = normalizeProtocols(protocols);
|
|
3176
|
+
const requested = normalizeProtocols(protocols === void 0 ? normalized.protocols : protocols);
|
|
3188
3177
|
const state = createSocketState();
|
|
3189
3178
|
state.isServer = false;
|
|
3190
3179
|
state.url = parsed.url;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ventiws",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.3",
|
|
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": {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"node": ">=22.12.0"
|
|
35
35
|
},
|
|
36
36
|
"optionalDependencies": {
|
|
37
|
-
"@ventiws/binding-darwin-arm64": "1.0.0-rc.
|
|
38
|
-
"@ventiws/binding-darwin-x64": "1.0.0-rc.
|
|
39
|
-
"@ventiws/binding-linux-arm64-gnu": "1.0.0-rc.
|
|
40
|
-
"@ventiws/binding-linux-x64-gnu": "1.0.0-rc.
|
|
41
|
-
"@ventiws/binding-linux-x64-musl": "1.0.0-rc.
|
|
37
|
+
"@ventiws/binding-darwin-arm64": "1.0.0-rc.3",
|
|
38
|
+
"@ventiws/binding-darwin-x64": "1.0.0-rc.3",
|
|
39
|
+
"@ventiws/binding-linux-arm64-gnu": "1.0.0-rc.3",
|
|
40
|
+
"@ventiws/binding-linux-x64-gnu": "1.0.0-rc.3",
|
|
41
|
+
"@ventiws/binding-linux-x64-musl": "1.0.0-rc.3"
|
|
42
42
|
}
|
|
43
43
|
}
|