quickwin 2026.5.2-3.145209

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 (83) hide show
  1. package/README.md +6 -0
  2. package/examples/pdf_preview.js +440 -0
  3. package/examples/pdf_preview.ts +470 -0
  4. package/examples/preact_demo.js +35 -0
  5. package/examples/preact_demo.tsx +49 -0
  6. package/examples/tray_demo.js +75 -0
  7. package/examples/tray_demo.tsx +79 -0
  8. package/lib/fetch.js +746 -0
  9. package/lib/fetch.ts +811 -0
  10. package/lib/polyfill.js +500 -0
  11. package/lib/polyfill.ts +454 -0
  12. package/lib/preact/hooks.js +287 -0
  13. package/lib/preact/hooks.ts +330 -0
  14. package/lib/preact/jsx-runtime.js +1 -0
  15. package/lib/preact/jsx-runtime.ts +2 -0
  16. package/lib/preact/jsx.d.ts +36 -0
  17. package/lib/preact/layout.js +153 -0
  18. package/lib/preact/layout.ts +183 -0
  19. package/lib/preact/preact.js +54 -0
  20. package/lib/preact/preact.ts +133 -0
  21. package/lib/preact/props.js +99 -0
  22. package/lib/preact/props.ts +119 -0
  23. package/lib/preact/render.js +320 -0
  24. package/lib/preact/render.ts +353 -0
  25. package/lib/websocket.js +540 -0
  26. package/lib/websocket.ts +574 -0
  27. package/package.json +32 -0
  28. package/quickwin.d.ts +657 -0
  29. package/test/add.wasm +0 -0
  30. package/test/complex.wasm +0 -0
  31. package/test/complex_imports.wasm +0 -0
  32. package/test/global_imports.wasm +0 -0
  33. package/test/import_func.wasm +0 -0
  34. package/test/imports.wasm +0 -0
  35. package/test/run.js +86 -0
  36. package/test/run.ts +90 -0
  37. package/test/sjlj.wasm +0 -0
  38. package/test/test_basic.js +7 -0
  39. package/test/test_basic.ts +9 -0
  40. package/test/test_brotli.js +48 -0
  41. package/test/test_brotli.ts +52 -0
  42. package/test/test_fetch_cache.js +131 -0
  43. package/test/test_fetch_cache.ts +141 -0
  44. package/test/test_ffi.js +157 -0
  45. package/test/test_ffi.ts +174 -0
  46. package/test/test_frame_encoding.js +128 -0
  47. package/test/test_frame_encoding.ts +132 -0
  48. package/test/test_helper.js +84 -0
  49. package/test/test_helper.ts +80 -0
  50. package/test/test_http_import.js +78 -0
  51. package/test/test_http_import.ts +74 -0
  52. package/test/test_mupdf_render.js +69 -0
  53. package/test/test_mupdf_render.ts +74 -0
  54. package/test/test_mupdf_twice.js +77 -0
  55. package/test/test_mupdf_twice.ts +81 -0
  56. package/test/test_mupdf_wasm.js +33 -0
  57. package/test/test_mupdf_wasm.ts +30 -0
  58. package/test/test_net_event.js +63 -0
  59. package/test/test_net_event.ts +59 -0
  60. package/test/test_net_fetch.js +153 -0
  61. package/test/test_net_fetch.ts +131 -0
  62. package/test/test_net_websocket.js +158 -0
  63. package/test/test_net_websocket.ts +144 -0
  64. package/test/test_polyfill.js +58 -0
  65. package/test/test_polyfill.ts +60 -0
  66. package/test/test_url.js +173 -0
  67. package/test/test_url.ts +183 -0
  68. package/test/test_wasm_basic.js +82 -0
  69. package/test/test_wasm_basic.ts +70 -0
  70. package/test/test_wasm_import_global.js +41 -0
  71. package/test/test_wasm_import_global.ts +39 -0
  72. package/test/test_wasm_sjlj.js +153 -0
  73. package/test/test_wasm_sjlj.ts +134 -0
  74. package/test/test_wasm_types.js +96 -0
  75. package/test/test_wasm_types.ts +108 -0
  76. package/test/types.wasm +0 -0
  77. package/tsconfig.json +18 -0
  78. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -0
  79. package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -0
  80. package/vendor/mupdf-wasm/mupdf-wasm.wasm +0 -0
  81. package/vendor/mupdf-wasm/mupdf.d.ts +939 -0
  82. package/vendor/mupdf-wasm/mupdf.js +3317 -0
  83. package/win-mingw64.exe +0 -0
@@ -0,0 +1,540 @@
1
+ import '../lib/polyfill.js';
2
+ import * as sock from 'sock';
3
+ import * as wolfssl from 'wolfssl';
4
+ import * as os from 'os';
5
+ // ── Constants ──
6
+ const MAGIC_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
7
+ var State;
8
+ (function (State) {
9
+ State[State["CONNECTING"] = 0] = "CONNECTING";
10
+ State[State["OPEN"] = 1] = "OPEN";
11
+ State[State["CLOSING"] = 2] = "CLOSING";
12
+ State[State["CLOSED"] = 3] = "CLOSED";
13
+ })(State || (State = {}));
14
+ var Opcode;
15
+ (function (Opcode) {
16
+ Opcode[Opcode["CONTINUATION"] = 0] = "CONTINUATION";
17
+ Opcode[Opcode["TEXT"] = 1] = "TEXT";
18
+ Opcode[Opcode["BINARY"] = 2] = "BINARY";
19
+ Opcode[Opcode["CLOSE"] = 8] = "CLOSE";
20
+ Opcode[Opcode["PING"] = 9] = "PING";
21
+ Opcode[Opcode["PONG"] = 10] = "PONG";
22
+ })(Opcode || (Opcode = {}));
23
+ // ── Random key generation ──
24
+ function generateKey() {
25
+ const bytes = new Uint8Array(16);
26
+ crypto.getRandomValues(bytes);
27
+ return btoa(String.fromCharCode(...bytes));
28
+ }
29
+ async function computeAccept(key) {
30
+ const data = new TextEncoder().encode(key + MAGIC_GUID);
31
+ const hash = await crypto.subtle.digest('SHA-1', data);
32
+ const hashBytes = new Uint8Array(hash);
33
+ return btoa(String.fromCharCode(...hashBytes));
34
+ }
35
+ // ── Frame helpers ──
36
+ function createFrame(opcode, payload) {
37
+ const maskingKey = new Uint8Array(4);
38
+ for (let i = 0; i < 4; i++)
39
+ maskingKey[i] = (Math.random() * 256) | 0;
40
+ let headerSize;
41
+ if (payload.length < 126) {
42
+ headerSize = 6;
43
+ }
44
+ else if (payload.length < 65536) {
45
+ headerSize = 8;
46
+ }
47
+ else {
48
+ headerSize = 14;
49
+ }
50
+ const buf = new ArrayBuffer(headerSize + payload.length);
51
+ const view = new Uint8Array(buf);
52
+ let off = 0;
53
+ view[off++] = 0x80 | opcode;
54
+ view[off++] = 0x80 | (payload.length < 126 ? payload.length :
55
+ payload.length < 65536 ? 126 : 127);
56
+ if (payload.length >= 126 && payload.length < 65536) {
57
+ view[off++] = (payload.length >> 8) & 0xFF;
58
+ view[off++] = payload.length & 0xFF;
59
+ }
60
+ else if (payload.length >= 65536) {
61
+ const len = payload.length;
62
+ const hi = Math.floor(len / 0x100000000) >>> 0;
63
+ const lo = len >>> 0;
64
+ view[off++] = (hi >> 24) & 0xFF;
65
+ view[off++] = (hi >> 16) & 0xFF;
66
+ view[off++] = (hi >> 8) & 0xFF;
67
+ view[off++] = hi & 0xFF;
68
+ view[off++] = (lo >> 24) & 0xFF;
69
+ view[off++] = (lo >> 16) & 0xFF;
70
+ view[off++] = (lo >> 8) & 0xFF;
71
+ view[off++] = lo & 0xFF;
72
+ }
73
+ view[off++] = maskingKey[0];
74
+ view[off++] = maskingKey[1];
75
+ view[off++] = maskingKey[2];
76
+ view[off++] = maskingKey[3];
77
+ for (let i = 0; i < payload.length; i++) {
78
+ view[off++] = payload[i] ^ maskingKey[i % 4];
79
+ }
80
+ return buf;
81
+ }
82
+ function createCloseFrame(code, reason) {
83
+ const reasonBytes = new Uint8Array(reason.length + 2);
84
+ reasonBytes[0] = (code >> 8) & 0xFF;
85
+ reasonBytes[1] = code & 0xFF;
86
+ for (let i = 0; i < reason.length; i++) {
87
+ reasonBytes[i + 2] = reason.charCodeAt(i) & 0xFF;
88
+ }
89
+ return createFrame(Opcode.CLOSE, reasonBytes);
90
+ }
91
+ function tryParseFrame(buffer) {
92
+ if (buffer.length < 2)
93
+ return null;
94
+ const b0 = buffer[0];
95
+ const b1 = buffer[1];
96
+ const fin = (b0 & 0x80) !== 0;
97
+ const opcode = b0 & 0x0F;
98
+ const masked = (b1 & 0x80) !== 0;
99
+ let payloadLen = b1 & 0x7F;
100
+ let offset = 2;
101
+ if (payloadLen === 126) {
102
+ if (buffer.length < 4)
103
+ return null;
104
+ payloadLen = (buffer[2] << 8) | buffer[3];
105
+ offset = 4;
106
+ }
107
+ else if (payloadLen === 127) {
108
+ if (buffer.length < 10)
109
+ return null;
110
+ let hi = 0, lo = 0;
111
+ for (let i = 0; i < 4; i++)
112
+ hi = (hi * 256 + buffer[offset + i]) >>> 0;
113
+ for (let i = 4; i < 8; i++)
114
+ lo = (lo * 256 + buffer[offset + i]) >>> 0;
115
+ payloadLen = hi * 0x100000000 + lo;
116
+ offset = 10;
117
+ }
118
+ if (masked)
119
+ offset += 4;
120
+ const totalLen = offset + payloadLen;
121
+ if (buffer.length < totalLen)
122
+ return null;
123
+ let payload;
124
+ if (masked) {
125
+ const maskKey = buffer.slice(offset - 4, offset);
126
+ payload = new Uint8Array(payloadLen);
127
+ for (let i = 0; i < payloadLen; i++) {
128
+ payload[i] = buffer[offset + i] ^ maskKey[i % 4];
129
+ }
130
+ }
131
+ else {
132
+ payload = buffer.slice(offset, offset + payloadLen);
133
+ }
134
+ return { opcode, payload, fin, totalLen };
135
+ }
136
+ class WebSocket {
137
+ url;
138
+ readyState = State.CONNECTING;
139
+ onopen = null;
140
+ onclose = null;
141
+ onerror = null;
142
+ onmessage = null;
143
+ _sock = null;
144
+ _ssl = null;
145
+ _ctx = null;
146
+ _readBuffer = new Uint8Array(0);
147
+ _state = State.CONNECTING;
148
+ _resolveOpen = null;
149
+ _processingHandshake = false;
150
+ static CONNECTING = State.CONNECTING;
151
+ static OPEN = State.OPEN;
152
+ static CLOSING = State.CLOSING;
153
+ static CLOSED = State.CLOSED;
154
+ _closeCode = 1005;
155
+ _closeReason = '';
156
+ _requestKey = '';
157
+ constructor(url) {
158
+ this.url = url;
159
+ this._connect();
160
+ }
161
+ send(data) {
162
+ if (this._state !== State.OPEN)
163
+ return;
164
+ let payload;
165
+ if (typeof data === 'string') {
166
+ const bytes = [];
167
+ for (let i = 0; i < data.length; i++) {
168
+ const c = data.charCodeAt(i);
169
+ if (c < 0x80) {
170
+ bytes.push(c);
171
+ }
172
+ else if (c < 0x800) {
173
+ bytes.push(0xC0 | (c >> 6), 0x80 | (c & 0x3F));
174
+ }
175
+ else {
176
+ bytes.push(0xE0 | (c >> 12), 0x80 | ((c >> 6) & 0x3F), 0x80 | (c & 0x3F));
177
+ }
178
+ }
179
+ payload = new Uint8Array(bytes);
180
+ const frame = createFrame(Opcode.TEXT, payload);
181
+ this._sendRaw(frame);
182
+ }
183
+ else if (data instanceof ArrayBuffer) {
184
+ payload = new Uint8Array(data);
185
+ const frame = createFrame(Opcode.BINARY, payload);
186
+ this._sendRaw(frame);
187
+ }
188
+ else if (data instanceof Uint8Array) {
189
+ const frame = createFrame(Opcode.BINARY, data);
190
+ this._sendRaw(frame);
191
+ }
192
+ }
193
+ close(code, reason) {
194
+ if (this._state === State.CLOSED || this._state === State.CLOSING)
195
+ return;
196
+ this._state = State.CLOSING;
197
+ const closeCode = code || 1000;
198
+ const closeReason = reason || '';
199
+ this._closeCode = closeCode;
200
+ this._closeReason = closeReason;
201
+ const frame = createCloseFrame(closeCode, closeReason);
202
+ this._sendRaw(frame);
203
+ this._cleanup();
204
+ this._setState(State.CLOSED);
205
+ }
206
+ _connect() {
207
+ let url;
208
+ try {
209
+ url = new URL(this.url);
210
+ }
211
+ catch {
212
+ const self = this;
213
+ os.setTimeout(() => {
214
+ self._fireError(new Error('Invalid WebSocket URL: ' + self.url));
215
+ self._setState(State.CLOSED);
216
+ }, 0);
217
+ return;
218
+ }
219
+ const host = url.hostname;
220
+ const isWSS = url.protocol === 'wss:';
221
+ const port = url.port ? parseInt(url.port, 10) : (isWSS ? 443 : 80);
222
+ const path = url.pathname;
223
+ const requestKey = generateKey();
224
+ this._requestKey = requestKey;
225
+ const request = ('GET ' + path + ' HTTP/1.1\r\n' +
226
+ 'Host: ' + host + ':' + port + '\r\n' +
227
+ 'Upgrade: websocket\r\n' +
228
+ 'Connection: Upgrade\r\n' +
229
+ 'Sec-WebSocket-Key: ' + requestKey + '\r\n' +
230
+ 'Sec-WebSocket-Version: 13\r\n' +
231
+ '\r\n');
232
+ let state = 'resolve';
233
+ let headerBuffer = '';
234
+ let resolved = false;
235
+ const cleanup = () => {
236
+ if (this._ssl) {
237
+ wolfssl.wolfSSL_free(this._ssl);
238
+ this._ssl = null;
239
+ }
240
+ if (this._ctx) {
241
+ wolfssl.wolfSSL_CTX_free(this._ctx);
242
+ this._ctx = null;
243
+ }
244
+ if (this._sock) {
245
+ sock.closesocket(this._sock);
246
+ this._sock = null;
247
+ }
248
+ };
249
+ const doError = (err) => {
250
+ if (resolved)
251
+ return;
252
+ resolved = true;
253
+ cleanup();
254
+ this._fireError(err);
255
+ if (this._state !== State.CLOSED)
256
+ this._setState(State.CLOSED);
257
+ };
258
+ this._sock = sock.socket();
259
+ if (!this._sock) {
260
+ const self = this;
261
+ os.setTimeout(() => {
262
+ self._fireError(new Error('Failed to create socket'));
263
+ if (self._state !== State.CLOSED)
264
+ self._setState(State.CLOSED);
265
+ }, 0);
266
+ return;
267
+ }
268
+ const fd = this._sock;
269
+ sock.set_on_event(fd, async (event) => {
270
+ if (this._processingHandshake)
271
+ return;
272
+ if (resolved && state === 'open') {
273
+ if (event.lNetworkEvents & sock.FD_READ) {
274
+ this._onData();
275
+ }
276
+ if (event.lNetworkEvents & sock.FD_CLOSE) {
277
+ this._cleanup();
278
+ this._setState(State.CLOSED);
279
+ }
280
+ return;
281
+ }
282
+ if (event.lNetworkEvents & sock.FD_CONNECT) {
283
+ const errCode = event.iErrorCode[0];
284
+ if (errCode !== 0) {
285
+ doError(new Error('Connection failed: ' + errCode));
286
+ return;
287
+ }
288
+ if (isWSS) {
289
+ const method = wolfssl.wolfTLSv1_2_client_method();
290
+ this._ctx = wolfssl.wolfSSL_CTX_new(method);
291
+ wolfssl.wolfSSL_CTX_set_verify(this._ctx, wolfssl.SSL_VERIFY_NONE);
292
+ this._ssl = wolfssl.wolfSSL_new(this._ctx);
293
+ if (!this._ssl) {
294
+ doError(new Error('SSL_new failed'));
295
+ return;
296
+ }
297
+ wolfssl.wolfSSL_set_fd(this._ssl, sock.get_fd(fd));
298
+ const sniHost = host;
299
+ if (sniHost)
300
+ wolfssl.wolfSSL_UseSNI(this._ssl, wolfssl.WOLFSSL_SNI_HOST_NAME, sniHost);
301
+ state = 'handshake';
302
+ }
303
+ else {
304
+ this._sendRawStr(request);
305
+ state = 'request';
306
+ }
307
+ }
308
+ if ((event.lNetworkEvents & sock.FD_READ) || (event.lNetworkEvents & sock.FD_WRITE)) {
309
+ if (state === 'handshake') {
310
+ if (!this._ssl) {
311
+ doError(new Error('TLS not initialized'));
312
+ return;
313
+ }
314
+ const ret = wolfssl.wolfSSL_connect(this._ssl);
315
+ if (ret === wolfssl.SSL_SUCCESS) {
316
+ this._sendRawStr(request);
317
+ state = 'request';
318
+ }
319
+ else {
320
+ const err = wolfssl.wolfSSL_get_error(this._ssl, ret);
321
+ if (err !== wolfssl.WOLFSSL_ERROR_WANT_READ &&
322
+ err !== wolfssl.WOLFSSL_ERROR_WANT_WRITE) {
323
+ doError(new Error('TLS handshake failed: ' + err));
324
+ }
325
+ }
326
+ }
327
+ else if (state === 'request') {
328
+ while (true) {
329
+ let data;
330
+ if (isWSS && this._ssl) {
331
+ data = wolfssl.wolfSSL_read(this._ssl, 8192);
332
+ }
333
+ else if (this._sock) {
334
+ data = sock.recv(this._sock, 8192);
335
+ }
336
+ else {
337
+ break;
338
+ }
339
+ if (!data || data.byteLength === 0)
340
+ break;
341
+ headerBuffer += _ab2str(data);
342
+ const headerEnd = headerBuffer.indexOf('\r\n\r\n');
343
+ if (headerEnd >= 0) {
344
+ const headerPart = headerBuffer.slice(0, headerEnd);
345
+ const lines = headerPart.split('\r\n');
346
+ const statusLine = lines[0];
347
+ const statusParts = statusLine.split(' ');
348
+ const statusCode = parseInt(statusParts[1], 10);
349
+ if (statusCode !== 101) {
350
+ doError(new Error('WebSocket handshake failed: HTTP ' + statusCode));
351
+ return;
352
+ }
353
+ let acceptHeader = '';
354
+ for (let i = 1; i < lines.length; i++) {
355
+ const line = lines[i];
356
+ const colonIdx2 = line.indexOf(':');
357
+ if (colonIdx2 >= 0) {
358
+ const name = line.slice(0, colonIdx2).toLowerCase().trim();
359
+ if (name === 'sec-websocket-accept') {
360
+ acceptHeader = line.slice(colonIdx2 + 1).trim();
361
+ break;
362
+ }
363
+ }
364
+ }
365
+ if (!acceptHeader) {
366
+ doError(new Error('WebSocket handshake missing Sec-WebSocket-Accept'));
367
+ return;
368
+ }
369
+ this._processingHandshake = true;
370
+ const expectedAccept = await computeAccept(requestKey);
371
+ if (acceptHeader !== expectedAccept) {
372
+ doError(new Error('WebSocket handshake invalid Sec-WebSocket-Accept'));
373
+ this._processingHandshake = false;
374
+ return;
375
+ }
376
+ state = 'open';
377
+ resolved = true;
378
+ this._processingHandshake = false;
379
+ this._setState(State.OPEN);
380
+ break;
381
+ }
382
+ }
383
+ }
384
+ }
385
+ if (event.lNetworkEvents & sock.FD_CLOSE) {
386
+ if (state !== 'open') {
387
+ doError(new Error('Connection closed'));
388
+ }
389
+ }
390
+ });
391
+ const ip = sock.resolve(host);
392
+ if (!ip) {
393
+ const self = this;
394
+ os.setTimeout(() => {
395
+ self._fireError(new Error('DNS resolution failed for: ' + host));
396
+ if (self._state !== State.CLOSED)
397
+ self._setState(State.CLOSED);
398
+ }, 0);
399
+ return;
400
+ }
401
+ sock.connect(fd, ip, port);
402
+ }
403
+ _sendRaw(buf) {
404
+ if (!this._sock)
405
+ return;
406
+ if (this._ssl) {
407
+ wolfssl.wolfSSL_write(this._ssl, buf);
408
+ }
409
+ else {
410
+ sock.send(this._sock, buf);
411
+ }
412
+ }
413
+ _sendRawStr(str) {
414
+ const buf = new ArrayBuffer(str.length);
415
+ const view = new Uint8Array(buf);
416
+ for (let i = 0; i < str.length; i++)
417
+ view[i] = str.charCodeAt(i) & 0xFF;
418
+ this._sendRaw(buf);
419
+ }
420
+ _onData() {
421
+ while (true) {
422
+ let data;
423
+ if (this._ssl) {
424
+ data = wolfssl.wolfSSL_read(this._ssl, 8192);
425
+ }
426
+ else if (this._sock) {
427
+ data = sock.recv(this._sock, 8192);
428
+ }
429
+ else {
430
+ break;
431
+ }
432
+ if (!data || data.byteLength === 0)
433
+ break;
434
+ const newBuf = new Uint8Array(this._readBuffer.length + data.byteLength);
435
+ newBuf.set(this._readBuffer);
436
+ newBuf.set(new Uint8Array(data), this._readBuffer.length);
437
+ this._readBuffer = newBuf;
438
+ while (true) {
439
+ const frame = tryParseFrame(this._readBuffer);
440
+ if (!frame)
441
+ break;
442
+ this._readBuffer = this._readBuffer.slice(frame.totalLen);
443
+ if (frame.opcode === Opcode.TEXT || frame.opcode === Opcode.BINARY) {
444
+ const event = new MessageEvent('message', { data: frame.opcode === Opcode.TEXT ? _ab2str(frame.payload.slice(0).buffer) : frame.payload.slice(0).buffer });
445
+ this._dispatchEvent('message', event);
446
+ }
447
+ else if (frame.opcode === Opcode.PING) {
448
+ const pong = createFrame(Opcode.PONG, frame.payload);
449
+ this._sendRaw(pong);
450
+ }
451
+ else if (frame.opcode === Opcode.PONG) {
452
+ }
453
+ else if (frame.opcode === Opcode.CLOSE) {
454
+ if (frame.payload.length >= 2) {
455
+ this._closeCode = (frame.payload[0] << 8) | frame.payload[1];
456
+ this._closeReason = _ab2str(frame.payload.slice(2).buffer);
457
+ }
458
+ this._cleanup();
459
+ this._setState(State.CLOSED);
460
+ return;
461
+ }
462
+ }
463
+ }
464
+ }
465
+ _setState(state) {
466
+ if (this._state === state)
467
+ return;
468
+ this.readyState = state;
469
+ this._state = state;
470
+ if (state === State.OPEN) {
471
+ this._dispatchEvent('open', new Event('open'));
472
+ }
473
+ else if (state === State.CLOSED) {
474
+ const event = new CloseEvent('close', { code: this._closeCode, reason: this._closeReason, wasClean: true });
475
+ this._dispatchEvent('close', event);
476
+ }
477
+ }
478
+ _dispatchEvent(type, event) {
479
+ if (type === 'open' && this.onopen)
480
+ this.onopen(event);
481
+ else if (type === 'close' && this.onclose)
482
+ this.onclose(event);
483
+ else if (type === 'error' && this.onerror)
484
+ this.onerror(event);
485
+ else if (type === 'message' && this.onmessage)
486
+ this.onmessage(event);
487
+ }
488
+ _fireError(err) {
489
+ this._dispatchEvent('error', new Event('error'));
490
+ }
491
+ _cleanup() {
492
+ if (this._ssl) {
493
+ wolfssl.wolfSSL_free(this._ssl);
494
+ this._ssl = null;
495
+ }
496
+ if (this._ctx) {
497
+ wolfssl.wolfSSL_CTX_free(this._ctx);
498
+ this._ctx = null;
499
+ }
500
+ if (this._sock) {
501
+ sock.closesocket(this._sock);
502
+ this._sock = null;
503
+ }
504
+ }
505
+ }
506
+ // ── Event/MessageEvent/CloseEvent polyfills ──
507
+ class Event {
508
+ type;
509
+ constructor(type, _init) { this.type = type; }
510
+ }
511
+ class MessageEvent {
512
+ type;
513
+ data;
514
+ constructor(type, init) {
515
+ this.type = type;
516
+ this.data = init.data;
517
+ }
518
+ }
519
+ class CloseEvent {
520
+ type;
521
+ code;
522
+ reason;
523
+ wasClean;
524
+ constructor(type, init) {
525
+ this.type = type;
526
+ this.code = init.code || 0;
527
+ this.reason = init.reason || '';
528
+ this.wasClean = init.wasClean || false;
529
+ }
530
+ }
531
+ // ── Utility ──
532
+ function _ab2str(buf) {
533
+ const view = new Uint8Array(buf);
534
+ let str = '';
535
+ for (let i = 0; i < view.length; i++)
536
+ str += String.fromCharCode(view[i]);
537
+ return str;
538
+ }
539
+ // ── Register globals ──
540
+ globalThis.WebSocket = WebSocket;