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