quickwin 3.0.0 → 3.0.2

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 (253) hide show
  1. package/README.en.md +3 -0
  2. package/README.md +2 -0
  3. package/lib/abort.d.ts +21 -0
  4. package/lib/assert.d.ts +2 -0
  5. package/lib/bitmap.d.ts +7 -0
  6. package/lib/cache-utils.d.ts +6 -0
  7. package/lib/certs.d.ts +1 -0
  8. package/lib/dpi.d.ts +9 -0
  9. package/lib/dumpRects.d.ts +2 -0
  10. package/lib/exec-log.d.ts +23 -0
  11. package/lib/fetch.d.ts +4 -0
  12. package/lib/ffi/bind.d.ts +70 -0
  13. package/lib/ffi/ctype.d.ts +117 -0
  14. package/lib/ffi/index.d.ts +2 -0
  15. package/lib/ffi/struct.d.ts +174 -0
  16. package/lib/http-common.d.ts +143 -0
  17. package/lib/http-server.d.ts +59 -0
  18. package/lib/polyfill.d.ts +22 -0
  19. package/lib/react-qw/components/Button.d.ts +9 -0
  20. package/lib/react-qw/components/CheckBox.d.ts +11 -0
  21. package/lib/react-qw/components/ComboBox.d.ts +11 -0
  22. package/lib/react-qw/components/DateTimePicker.d.ts +15 -0
  23. package/lib/react-qw/components/Input.d.ts +15 -0
  24. package/lib/react-qw/components/Link.d.ts +12 -0
  25. package/lib/react-qw/components/ListBox.d.ts +13 -0
  26. package/lib/react-qw/components/ListView.d.ts +37 -0
  27. package/lib/react-qw/components/PathPicker.d.ts +23 -0
  28. package/lib/react-qw/components/ProgressBar.d.ts +9 -0
  29. package/lib/react-qw/components/RadioButton.d.ts +11 -0
  30. package/lib/react-qw/components/ScrollView.d.ts +9 -0
  31. package/lib/react-qw/components/Slider.d.ts +12 -0
  32. package/lib/react-qw/components/Tab.d.ts +14 -0
  33. package/lib/react-qw/components/Tooltip.d.ts +7 -0
  34. package/lib/react-qw/components/TreeView.d.ts +18 -0
  35. package/lib/react-qw/components/index.d.ts +16 -0
  36. package/lib/react-qw/index.d.ts +24 -0
  37. package/lib/react-qw/layout.d.ts +29 -0
  38. package/lib/react-qw/nmhdr.d.ts +1 -0
  39. package/lib/react-qw/props.d.ts +2 -0
  40. package/lib/react-qw/reconciler.d.ts +21 -0
  41. package/lib/stream.d.ts +20 -0
  42. package/lib/text-codec.d.ts +39 -0
  43. package/lib/text-measure.d.ts +14 -0
  44. package/lib/url.d.ts +54 -0
  45. package/lib/websocket.d.ts +73 -0
  46. package/lib/windows/{advapi32.ts → advapi32.d.ts} +6 -21
  47. package/lib/windows/comctl32.d.ts +14 -0
  48. package/lib/windows/comdlg32.d.ts +4 -0
  49. package/lib/windows/{gdi32.ts → gdi32.d.ts} +161 -98
  50. package/lib/windows/{kernel32.ts → kernel32.d.ts} +138 -115
  51. package/lib/windows/{msimg32.ts → msimg32.d.ts} +11 -14
  52. package/lib/windows/ntdll.d.ts +5 -0
  53. package/lib/windows/ole32.d.ts +3 -0
  54. package/lib/windows/{psapi.ts → psapi.d.ts} +9 -18
  55. package/lib/windows/{shell32.ts → shell32.d.ts} +25 -24
  56. package/lib/windows/structs.d.ts +3141 -0
  57. package/lib/windows/{user32.ts → user32.d.ts} +196 -193
  58. package/lib/windows/version.d.ts +27 -0
  59. package/lib/windows/{winspool.ts → winspool.d.ts} +18 -32
  60. package/package.json +15 -8
  61. package/qwin-nowasm-x86.exe +0 -0
  62. package/qwin-nowasm.exe +0 -0
  63. package/qwin-x86.exe +0 -0
  64. package/qwin.exe +0 -0
  65. package/examples/PdfCanvas.js +0 -38
  66. package/examples/PdfCanvas.tsx +0 -54
  67. package/examples/execd/main.js +0 -23379
  68. package/examples/execd/tray.js +0 -192
  69. package/examples/execd/worker.js +0 -122
  70. package/examples/pdf_preview2.js +0 -398
  71. package/examples/pdf_preview2.ts +0 -353
  72. package/examples/pdf_viewer.js +0 -22515
  73. package/examples/pdf_viewer.tsx +0 -228
  74. package/examples/test_gallery.js +0 -18692
  75. package/examples/test_gallery.tsx +0 -291
  76. package/examples/test_react_lazy.js +0 -18560
  77. package/examples/test_react_lazy.tsx +0 -65
  78. package/examples/test_react_listbox.js +0 -18562
  79. package/examples/test_react_listbox.tsx +0 -73
  80. package/examples/test_root_window.js +0 -18532
  81. package/examples/test_root_window.tsx +0 -41
  82. package/examples/test_tab.js +0 -18563
  83. package/examples/test_tab.tsx +0 -77
  84. package/examples/tray_demo.js +0 -86
  85. package/examples/tray_demo.tsx +0 -89
  86. package/lib/abort.ts +0 -69
  87. package/lib/assert.ts +0 -7
  88. package/lib/bitmap.ts +0 -39
  89. package/lib/cache-utils.ts +0 -40
  90. package/lib/certs.ts +0 -258
  91. package/lib/dpi.ts +0 -31
  92. package/lib/dumpRects.ts +0 -34
  93. package/lib/exec-log.ts +0 -30
  94. package/lib/fetch.ts +0 -589
  95. package/lib/ffi/bind.ts +0 -540
  96. package/lib/ffi/ctype.ts +0 -371
  97. package/lib/ffi/index.ts +0 -6
  98. package/lib/ffi/struct.ts +0 -780
  99. package/lib/http-common.ts +0 -441
  100. package/lib/http-server.ts +0 -604
  101. package/lib/polyfill.ts +0 -171
  102. package/lib/react-qw/components/index.ts +0 -17
  103. package/lib/react-qw/index.ts +0 -96
  104. package/lib/react-qw/layout.ts +0 -111
  105. package/lib/react-qw/nmhdr.ts +0 -7
  106. package/lib/react-qw/props.ts +0 -41
  107. package/lib/react-qw/reconciler.ts +0 -379
  108. package/lib/stream.ts +0 -22
  109. package/lib/text-codec.ts +0 -171
  110. package/lib/text-measure.ts +0 -44
  111. package/lib/url.ts +0 -201
  112. package/lib/websocket.ts +0 -600
  113. package/lib/windows/comctl32.ts +0 -20
  114. package/lib/windows/comdlg32.ts +0 -10
  115. package/lib/windows/ntdll.ts +0 -11
  116. package/lib/windows/ole32.ts +0 -9
  117. package/lib/windows/structs.ts +0 -807
  118. package/lib/windows/version.ts +0 -35
  119. package/test/add.wasm +0 -0
  120. package/test/bench_ffi_callraw.js +0 -235
  121. package/test/bench_ffi_callraw.ts +0 -235
  122. package/test/bench_ffi_overhead.js +0 -139
  123. package/test/bench_ffi_overhead.ts +0 -141
  124. package/test/bench_sendmessage.js +0 -104
  125. package/test/bench_sendmessage.ts +0 -100
  126. package/test/bench_sendmessage_nonnull.js +0 -187
  127. package/test/bench_sendmessage_nonnull.ts +0 -188
  128. package/test/complex.wasm +0 -0
  129. package/test/complex_imports.wasm +0 -0
  130. package/test/debug/http_server_e2e.js +0 -31
  131. package/test/debug/minwasm.js +0 -52
  132. package/test/debug/mupdf_debug.js +0 -86
  133. package/test/debug/wasm.js +0 -122
  134. package/test/debug/wasm_export_debug.js +0 -54
  135. package/test/debug/wasm_names.js +0 -36
  136. package/test/example.pdf +0 -0
  137. package/test/global_imports.wasm +0 -0
  138. package/test/import_func.wasm +0 -0
  139. package/test/imports.wasm +0 -0
  140. package/test/mupdf-cdn.js +0 -40
  141. package/test/mupdf-cdn.ts +0 -43
  142. package/test/run.js +0 -111
  143. package/test/run.ts +0 -116
  144. package/test/sjlj.wasm +0 -0
  145. package/test/test_basic.js +0 -7
  146. package/test/test_basic.ts +0 -9
  147. package/test/test_basic_fetch.js +0 -63
  148. package/test/test_basic_fetch.ts +0 -59
  149. package/test/test_brotli.js +0 -48
  150. package/test/test_brotli.ts +0 -52
  151. package/test/test_fetch_cache.js +0 -169
  152. package/test/test_fetch_cache.ts +0 -187
  153. package/test/test_ffi.js +0 -157
  154. package/test/test_ffi.ts +0 -170
  155. package/test/test_ffi_abi.js +0 -290
  156. package/test/test_ffi_abi.ts +0 -274
  157. package/test/test_ffi_array.js +0 -116
  158. package/test/test_ffi_array.ts +0 -141
  159. package/test/test_ffi_bind.js +0 -656
  160. package/test/test_ffi_bind.ts +0 -670
  161. package/test/test_ffi_struct.js +0 -779
  162. package/test/test_ffi_struct.ts +0 -828
  163. package/test/test_ffi_struct_layout.js +0 -495
  164. package/test/test_ffi_struct_layout.ts +0 -543
  165. package/test/test_ffi_user32.js +0 -103
  166. package/test/test_ffi_user32.ts +0 -113
  167. package/test/test_ffi_windows.js +0 -172
  168. package/test/test_ffi_windows.ts +0 -187
  169. package/test/test_files.js +0 -102
  170. package/test/test_files.ts +0 -121
  171. package/test/test_frame_encoding.js +0 -128
  172. package/test/test_frame_encoding.ts +0 -132
  173. package/test/test_gdi_draw.js +0 -681
  174. package/test/test_gdi_draw.ts +0 -725
  175. package/test/test_helper.js +0 -91
  176. package/test/test_helper.ts +0 -88
  177. package/test/test_http_import.js +0 -96
  178. package/test/test_http_import.ts +0 -93
  179. package/test/test_http_server.js +0 -214
  180. package/test/test_http_server.ts +0 -211
  181. package/test/test_import_dir.js +0 -4
  182. package/test/test_import_dir.ts +0 -5
  183. package/test/test_ipv6.js +0 -134
  184. package/test/test_ipv6.ts +0 -130
  185. package/test/test_jsx.js +0 -1314
  186. package/test/test_meta_url.js +0 -3
  187. package/test/test_meta_url.ts +0 -4
  188. package/test/test_mupdf_render.js +0 -51
  189. package/test/test_mupdf_render.ts +0 -55
  190. package/test/test_mupdf_twice.js +0 -62
  191. package/test/test_mupdf_twice.ts +0 -65
  192. package/test/test_mupdf_wasm.js +0 -39
  193. package/test/test_mupdf_wasm.ts +0 -32
  194. package/test/test_net_event.js +0 -57
  195. package/test/test_net_event.ts +0 -56
  196. package/test/test_net_fetch.js +0 -384
  197. package/test/test_net_fetch.ts +0 -373
  198. package/test/test_net_websocket.js +0 -180
  199. package/test/test_net_websocket.ts +0 -164
  200. package/test/test_polyfill.js +0 -144
  201. package/test/test_polyfill.ts +0 -159
  202. package/test/test_printer.js +0 -137
  203. package/test/test_printer.ts +0 -148
  204. package/test/test_proc.js +0 -141
  205. package/test/test_proc.ts +0 -163
  206. package/test/test_react_complex.js +0 -18678
  207. package/test/test_react_counter.js +0 -18555
  208. package/test/test_react_flex.js +0 -18871
  209. package/test/test_react_hidden.js +0 -18560
  210. package/test/test_react_listview_link.js +0 -18590
  211. package/test/test_react_notify.js +0 -18797
  212. package/test/test_react_render.js +0 -18550
  213. package/test/test_registry.js +0 -74
  214. package/test/test_registry.ts +0 -95
  215. package/test/test_sysinfo.js +0 -122
  216. package/test/test_sysinfo.ts +0 -141
  217. package/test/test_url.js +0 -173
  218. package/test/test_url.ts +0 -183
  219. package/test/test_wasm_basic.js +0 -85
  220. package/test/test_wasm_basic.ts +0 -73
  221. package/test/test_wasm_import_global.js +0 -41
  222. package/test/test_wasm_import_global.ts +0 -45
  223. package/test/test_wasm_sjlj.js +0 -145
  224. package/test/test_wasm_sjlj.ts +0 -125
  225. package/test/test_wasm_types.js +0 -97
  226. package/test/test_wasm_types.ts +0 -124
  227. package/test/test_wolfssl_read.js +0 -203
  228. package/test/test_wolfssl_read.ts +0 -184
  229. package/test/test_worker.js +0 -21
  230. package/test/test_worker.ts +0 -24
  231. package/test/test_worker_concurrent.js +0 -45
  232. package/test/test_worker_concurrent.ts +0 -56
  233. package/test/test_worker_data_url.js +0 -46
  234. package/test/test_worker_data_url.ts +0 -51
  235. package/test/test_worker_fetch.js +0 -26
  236. package/test/test_worker_fetch.ts +0 -27
  237. package/test/test_worker_net.js +0 -69
  238. package/test/test_worker_net.ts +0 -66
  239. package/test/test_worker_wasm_concurrent.js +0 -44
  240. package/test/test_worker_wasm_concurrent.ts +0 -45
  241. package/test/types.wasm +0 -0
  242. package/test/worker_fetch.js +0 -13
  243. package/test/worker_fetch.ts +0 -13
  244. package/test/worker_helper.js +0 -1
  245. package/test/worker_helper.ts +0 -1
  246. package/test/worker_http.js +0 -18
  247. package/test/worker_http.ts +0 -20
  248. package/test/worker_import_lib.js +0 -12
  249. package/test/worker_import_lib.ts +0 -13
  250. package/test/worker_module.js +0 -16
  251. package/test/worker_module.ts +0 -17
  252. package/test/worker_wasm_concurrent.js +0 -22
  253. package/test/worker_wasm_concurrent.ts +0 -22
@@ -1,373 +0,0 @@
1
- import '../lib/fetch.js'
2
- import * as std from 'std'
3
- import * as os from 'os'
4
- import { Tester } from './test_helper.js'
5
-
6
- export const suite = {
7
- name: 'net-fetch',
8
- run: async (t: Tester) => {
9
- function assert(name: string, ok: boolean): void {
10
- if (ok) { t.ok++; std.printf(' PASS: %s\n', name) }
11
- else { t.fail++; std.printf(' FAIL: %s\n', name) }
12
- }
13
-
14
- // Retry: public-site reachability (baidu/jsdelivr/esm.sh) depends on
15
- // CI network conditions, and a single dropped connection must not be
16
- // reported as a certificate regression. The last error is printed so a
17
- // real failure is still diagnosable.
18
- async function safeFetch(url: any, init?: RequestInit) {
19
- let last = 'unknown'
20
- for (let attempt = 0; attempt < 3; attempt++) {
21
- try {
22
- return await fetch(url, init)
23
- } catch (e) {
24
- last = String((e as Error)?.message || e)
25
- if (attempt < 2) os.sleep(500)
26
- }
27
- }
28
- std.printf(' fetch %s failed after 3 attempts: %s\n', String(url), last)
29
- return null
30
- }
31
- const BASE = 'http://localhost:18923'
32
-
33
- // ── Plain HTTP GET (regression: slot 0 + !s bug) ──
34
- t.section('plain HTTP')
35
- const r0 = await safeFetch(BASE + '/', { timeout: 5000 })
36
- if (r0) {
37
- assert('plain HTTP status 200', r0.status === 200)
38
- const body = await r0.text()
39
- assert('plain HTTP body non-empty', body.length > 0)
40
- assert('plain HTTP body matches', body === 'hello from test server')
41
- } else {
42
- assert('plain HTTP endpoint reachable', false)
43
- }
44
-
45
- // ── Basic HTTP GET ──
46
- t.section('HTTP GET')
47
- const r1 = await safeFetch(BASE + '/any')
48
- if (r1) {
49
- assert('status received', r1.status > 0)
50
- const body = await r1.text()
51
- assert('body received', body.length > 0)
52
- } else {
53
- assert('local server reachable', false)
54
- }
55
-
56
- // ── Query string ──
57
- t.section('query string')
58
- const rq = await safeFetch(BASE + '/any?foo=bar&baz=42')
59
- if (rq) {
60
- const body = JSON.parse(await rq.text())
61
- assert('query args received', body.args !== undefined)
62
- assert('?foo=bar preserved', body.args.foo === 'bar')
63
- assert('?baz=42 preserved', body.args.baz === '42')
64
- } else {
65
- assert('query string endpoint reachable', false)
66
- }
67
-
68
- // ── Request constructor ──
69
- t.section('Request constructor')
70
- {
71
- const r = new Request('https://httpbun.com/any?x=1&y=2', { method: 'POST', headers: { 'X-Test': 'val' }, body: 'hello' })
72
- assert('Request.url', r.url === 'https://httpbun.com/any?x=1&y=2')
73
- assert('Request.method', r.method === 'POST')
74
- assert('Request.headers get', r.headers.get('x-test') === 'val')
75
- assert('Request.body is ReadableStream', r.body !== null && typeof r.body.getReader === 'function')
76
- }
77
- {
78
- const r1 = new Request('https://example.com/path')
79
- assert('Request default method GET', r1.method === 'GET')
80
- assert('Request default redirect follow', r1.redirect === 'follow')
81
- }
82
- {
83
- const r2 = new Request('https://httpbun.com/any')
84
- const r3 = new Request(r2, { method: 'PUT' })
85
- assert('Request clone same url', r3.url === r2.url)
86
- assert('Request clone override method', r3.method === 'PUT')
87
- }
88
- {
89
- const r = await safeFetch(new Request(BASE + '/any', { method: 'POST', headers: { 'Content-Type': 'text/plain' }, body: 'req-class' }))
90
- if (r) {
91
- const body = JSON.parse(await r.text())
92
- assert('fetch(Request) status', r.status > 0)
93
- assert('fetch(Request) method POST', body.method === 'POST')
94
- assert('fetch(Request) body received', body.data === 'req-class')
95
- assert('fetch(Request) content-type', body.headers['content-type'] === 'text/plain')
96
- } else {
97
- assert('fetch(Request) endpoint reachable', false)
98
- }
99
- }
100
-
101
- // ── body / bodyUsed / stream ──
102
- t.section('body / bodyUsed / stream')
103
- const r2 = await safeFetch(BASE + '/any')
104
- if (r2) {
105
- assert('r.body exists', typeof r2.body === 'object' && r2.body !== null)
106
- assert('bodyUsed false before read', r2.bodyUsed === false)
107
-
108
- const reader = r2.body!.getReader()
109
- let total = 0
110
- while (true) {
111
- const { done, value } = await reader.read()
112
- if (done) break
113
- total += value.length
114
- }
115
- assert('reader streams all bytes', total > 0)
116
- assert('bodyUsed true after stream', r2.bodyUsed === true)
117
- } else {
118
- assert('body test endpoint reachable', false)
119
- }
120
-
121
- // ── text/json/arrayBuffer ──
122
- t.section('text / json / arrayBuffer')
123
- const r3 = await safeFetch(BASE + '/any')
124
- if (r3) {
125
- const text = await r3.text()
126
- assert('text() returns string', typeof text === 'string' && text.length > 0)
127
- assert('bodyUsed true after text()', r3.bodyUsed === true)
128
-
129
- const r4 = await safeFetch(BASE + '/any')
130
- if (r4) {
131
- const buf = await r4.arrayBuffer()
132
- assert('arrayBuffer() returns bytes', buf.byteLength > 0)
133
- }
134
- } else {
135
- assert('text/json/ab endpoint reachable', false)
136
- }
137
-
138
- // double text() throws
139
- t.section('bodyUsed throws')
140
- const r5 = await safeFetch(BASE + '/any')
141
- if (r5) {
142
- await r5.text()
143
- try { await r5.text(); assert('double text() throws', false) }
144
- catch (e: unknown) { assert('double text() throws TypeError', (e as Error).message === 'Body already used') }
145
- }
146
-
147
- // ── POST ──
148
- t.section('POST')
149
- const r6 = await safeFetch(BASE + '/any', {
150
- method: 'POST',
151
- headers: { 'Content-Type': 'application/json' },
152
- body: JSON.stringify({ msg: 'hello' })
153
- })
154
- if (r6) {
155
- const body = await r6.text()
156
- assert('POST body received', body.length > 0)
157
- } else {
158
- assert('POST endpoint reachable', false)
159
- }
160
-
161
- // ── Headers (local, no network) ──
162
- t.section('Headers (local)')
163
- const h = new Headers({ 'Content-Type': 'text/html', 'X-Custom': 'hello', 'Accept': '*/*' })
164
- let c = 0
165
- for (const [k, v] of h) { c++; if (typeof k !== 'string' || typeof v !== 'string') assert('for...of type', false) }
166
- assert('for...of yields entries', c === 3)
167
- assert('get()', h.get('content-type') === 'text/html')
168
- assert('get() case-insensitive', h.get('Content-Type') === 'text/html')
169
- assert('has()', h.has('x-custom'))
170
- assert('set() overwrites', (h.set('accept', 'application/json'), h.get('accept') === 'application/json'))
171
- assert('append() adds', (h.append('accept', 'text/plain'), h.get('accept') === 'application/json, text/plain'))
172
- assert('delete()', (h.delete('accept'), h.has('accept') === false))
173
-
174
- const h2 = new Headers({ 'a': '1', 'b': '2' })
175
- let ec = 0; for (const _ of h2.entries()) { ec++ }
176
- assert('entries()', ec === 2)
177
- let kc = 0; for (const _ of h2.keys()) { kc++ }
178
- assert('keys()', kc === 2)
179
- let vc = 0; for (const _ of h2.values()) { vc++ }
180
- assert('values()', vc === 2)
181
-
182
- // ── Chunked Transfer-Encoding ──
183
- t.section('chunked transfer encoding')
184
- const r8 = await safeFetch(BASE + '/anything')
185
- if (r8) {
186
- assert('chunked status 200', r8.status === 200)
187
- const body = await r8.json<{ url: string }>()
188
- assert('chunked json has url', typeof body.url === 'string' && body.url.length > 0)
189
- } else {
190
- assert('local server reachable', false)
191
- }
192
-
193
- // ── stream cancel ──
194
- t.section('stream cancel')
195
- const r7 = await safeFetch(BASE + '/any')
196
- if (r7) {
197
- r7.body!.cancel()
198
- assert('cancel() succeeds', true)
199
- // After cancel: buffered chunks still readable, then done
200
- const reader = r7.body!.getReader()
201
- let finalDone = false
202
- while (true) {
203
- const { done } = await reader.read()
204
- if (done) { finalDone = true; break }
205
- }
206
- assert('reader done after cancel', finalDone === true)
207
- } else {
208
- assert('cancel test endpoint reachable', false)
209
- }
210
-
211
- // ── UTF-8 Chinese text body ──
212
- t.section('utf-8 chinese text')
213
- const r9 = await safeFetch(BASE + '/base64/5Lit5paH')
214
- if (r9) {
215
- assert('chinese status 200', r9.status === 200)
216
- const body = await r9.text()
217
- assert('chinese body is 中文', body === '\u4e2d\u6587')
218
- assert('chinese body length=2', body.length === 2)
219
- } else {
220
- assert('local server reachable', false)
221
- }
222
-
223
- // ── POST with Chinese body ──
224
- t.section('post with chinese body')
225
- const r10 = await safeFetch(BASE + '/post', {
226
- method: 'POST',
227
- body: '\u4e2d\u6587',
228
- headers: { 'Content-Type': 'text/plain; charset=utf-8' }
229
- })
230
- if (r10) {
231
- assert('post status 200', r10.status === 200)
232
- const body = await r10.text()
233
- assert('post body contains 中文', body.includes('\u4e2d\u6587'))
234
- } else {
235
- assert('local server reachable', false)
236
- }
237
-
238
- // ── HTTPS fetch ──
239
- t.section('HTTPS fetch')
240
- const r11 = await safeFetch('https://localhost:18924/', { timeout: 5000, rejectUnauthorized: false })
241
- if (r11) {
242
- assert('https status 200', r11.status === 200)
243
- const body = await r11.text()
244
- assert('https body matches', body === 'hello from test server')
245
- } else {
246
- assert('https endpoint reachable', false)
247
- }
248
-
249
- // ── GlobalSign R3 root chain (regression: ASN_SIG_CONFIRM_E -155) ──
250
- // baidu.com and jsdelivr terminate in GlobalSign's 2018 re-keyed R3,
251
- // which reuses the self-signed R3's subject-key-identifier with a
252
- // different public key. Shipping the self-signed R3 in lib/certs.ts
253
- // made wolfSSL match the wrong key and fail the handshake with -155.
254
- // Both are public CDNs: a 500/429 or a dropped connection then counts
255
- // as a retried attempt, not an immediate failure — only a persistent
256
- // failure (cert regression or long-lived outage) should fail CI.
257
- t.section('GlobalSign R3 root chain')
258
- {
259
- async function fetchR3(url: any): Promise<string | null> {
260
- const init = { timeout: 10000 }
261
- for (let attempt = 0; attempt < 3; attempt++) {
262
- try {
263
- const r = await fetch(url, init)
264
- const body = await r.text()
265
- if (r.status === 200 && body.length > 0) return body
266
- std.printf(' fetch %s status=%s len=%d (attempt %d)\n',
267
- String(url), String(r.status), body.length, attempt)
268
- } catch (e) {
269
- std.printf(' fetch %s error: %s (attempt %d)\n',
270
- String(url), String((e as Error)?.message || e), attempt)
271
- }
272
- os.sleep(1000)
273
- }
274
- return null
275
- }
276
-
277
- const bb = await fetchR3('https://www.baidu.com/')
278
- if (bb) {
279
- assert('baidu (GlobalSign R3) status', true)
280
- assert('baidu (GlobalSign R3) body non-empty', bb.length > 0)
281
- } else {
282
- assert('baidu (GlobalSign R3) reachable', false)
283
- }
284
-
285
- const JSDELIVR_HOSTS = ['cdn.jsdelivr.net', 'fastly.jsdelivr.net', 'gcore.jsdelivr.net']
286
- for (const host of JSDELIVR_HOSTS) {
287
- const bj = await fetchR3('https://' + host + '/npm/left-pad@1.3.0')
288
- if (bj) {
289
- assert('jsdelivr (GlobalSign R3) status', true)
290
- assert('jsdelivr (GlobalSign R3) body non-empty', bj.length > 0)
291
- break
292
- }
293
- if (host !== JSDELIVR_HOSTS[JSDELIVR_HOSTS.length - 1]) {
294
- std.printf(' jsdelivr host %s failed, trying next\n', host)
295
- } else {
296
- assert('jsdelivr (GlobalSign R3) reachable', false)
297
- }
298
- }
299
- }
300
-
301
- // ── large HTTPS body (wolfSSL_read int contract) ──
302
- // read returns the raw int; -1/WANT_READ must never leak into
303
- // byte accounting (regression: a leaked -1 made receivedBytes
304
- // NaN → stream never closed → hang).
305
- t.section('large HTTPS body')
306
- {
307
- const LARGE = 200000
308
- try {
309
- const r = await Promise.race([
310
- fetch(`https://localhost:18924/large/${LARGE}`, { timeout: 15000, rejectUnauthorized: false }),
311
- new Promise<never>((_, rej) =>
312
- os.setTimeout(() => rej(new Error('Body hang watchdog 15s')), 15000)
313
- )
314
- ])
315
- assert('large HTTPS status 200', r.status === 200)
316
- const buf = await Promise.race([
317
- r.arrayBuffer(),
318
- new Promise<never>((_, rej) =>
319
- os.setTimeout(() => rej(new Error('arrayBuffer hang watchdog 15s')), 15000)
320
- )
321
- ])
322
- assert('large HTTPS byteLength exact', buf.byteLength === LARGE)
323
- } catch (e: unknown) {
324
- assert('large HTTPS completed without hang (' + String((e as Error).message) + ')', false)
325
- }
326
- }
327
-
328
- // ── large HTTP body (sock.recv path same null contract) ──
329
- t.section('large HTTP body')
330
- {
331
- const LARGE = 200000
332
- try {
333
- const r = await Promise.race([
334
- fetch(`http://localhost:18923/large/${LARGE}`, { timeout: 15000 }),
335
- new Promise<never>((_, rej) =>
336
- os.setTimeout(() => rej(new Error('Body hang watchdog 15s')), 15000)
337
- )
338
- ])
339
- assert('large HTTP status 200', r.status === 200)
340
- const buf = await Promise.race([
341
- r.arrayBuffer(),
342
- new Promise<never>((_, rej) =>
343
- os.setTimeout(() => rej(new Error('arrayBuffer hang watchdog 15s')), 15000)
344
- )
345
- ])
346
- assert('large HTTP byteLength exact', buf.byteLength === LARGE)
347
- } catch (e: unknown) {
348
- assert('large HTTP completed (' + String((e as Error).message) + ')', false)
349
- }
350
- }
351
-
352
- // ── body timeout after headers (armTimeout after doResolve) ──
353
- t.section('HTTPS body timeout')
354
- {
355
- let rejected = false
356
- let msg = ''
357
- try {
358
- const r = await fetch('https://localhost:18924/stall', { timeout: 2000, rejectUnauthorized: false })
359
- await Promise.race([
360
- r.arrayBuffer(),
361
- new Promise<never>((_, rej) =>
362
- os.setTimeout(() => rej(new Error('Body timeout')), 8000)
363
- )
364
- ])
365
- } catch (e: unknown) {
366
- rejected = true
367
- msg = String((e as Error).message)
368
- }
369
- assert('stall body rejects (timeout)', rejected)
370
- assert('stall error mentions timeout', /timeout/i.test(msg))
371
- }
372
- }
373
- }
@@ -1,180 +0,0 @@
1
- import '../lib/websocket.js';
2
- import * as std from 'std';
3
- import * as os from 'os';
4
- export const suite = {
5
- name: 'net-websocket',
6
- run: async (t) => {
7
- function assert(name, ok) {
8
- if (ok) {
9
- t.ok++;
10
- std.printf(' PASS: %s\n', name);
11
- }
12
- else {
13
- t.fail++;
14
- std.printf(' FAIL: %s\n', name);
15
- }
16
- }
17
- let testId = 0;
18
- const resolvers = {};
19
- function waitForTest() {
20
- const id = ++testId;
21
- const promise = new Promise((resolve) => { resolvers[id] = resolve; });
22
- return { promise, id };
23
- }
24
- function finishTest(id) {
25
- const r = resolvers[id];
26
- if (r) {
27
- delete resolvers[id];
28
- r();
29
- }
30
- }
31
- // ── wss basic ──
32
- t.section('wss basic text message');
33
- const p1 = waitForTest();
34
- let opened = false, received = false, closed = false;
35
- const ws1 = new WebSocket("ws://localhost:18923");
36
- ws1.onopen = () => { opened = true; ws1.send("Hello WebSocket!"); };
37
- ws1.onmessage = () => { received = true; ws1.close(1000, "done"); };
38
- ws1.onclose = (e) => {
39
- closed = true;
40
- assert('onopen fired', opened);
41
- assert('onmessage received', received);
42
- assert('close code=1000', e.code === 1000);
43
- assert('wasClean', e.wasClean);
44
- finishTest(p1.id);
45
- };
46
- ws1.onerror = () => { assert('no error', false); finishTest(p1.id); };
47
- const timer1 = os.setTimeout(() => { if (!closed) {
48
- assert('no timeout', false);
49
- finishTest(p1.id);
50
- } }, 10000);
51
- await p1.promise;
52
- os.clearTimeout(timer1);
53
- // ── binary send ──
54
- t.section('binary message (ArrayBuffer)');
55
- const pBin = waitForTest();
56
- const wsBin = new WebSocket("ws://localhost:18923");
57
- let binClose = false;
58
- wsBin.onopen = () => {
59
- const buf = new ArrayBuffer(5);
60
- const v = new Uint8Array(buf);
61
- v[0] = 0x48;
62
- v[1] = 0x65;
63
- v[2] = 0x6C;
64
- v[3] = 0x6C;
65
- v[4] = 0x6F;
66
- wsBin.send(buf);
67
- wsBin.send("binary-send-check");
68
- };
69
- let binMsgCount = 0;
70
- wsBin.onmessage = () => { binMsgCount++; if (binMsgCount >= 2)
71
- wsBin.close(1000, "done"); };
72
- wsBin.onclose = () => { binClose = true; assert('binary send completed', true); finishTest(pBin.id); };
73
- wsBin.onerror = () => { assert('no error', false); finishTest(pBin.id); };
74
- const timerBin = os.setTimeout(() => { if (!binClose)
75
- assert('binary no crash (timeout)', true); finishTest(pBin.id); }, 10000);
76
- await pBin.promise;
77
- os.clearTimeout(timerBin);
78
- // ── extended length frame ──
79
- t.section('extended length frame (>65535 bytes)');
80
- const pExt = waitForTest();
81
- const wsExt = new WebSocket("ws://localhost:18923");
82
- let extClose = false;
83
- wsExt.onopen = () => { wsExt.send("X".repeat(66000)); };
84
- wsExt.onmessage = () => { wsExt.close(1000, "done"); };
85
- wsExt.onclose = () => { extClose = true; assert('extended frame sent', true); finishTest(pExt.id); };
86
- wsExt.onerror = () => { assert('no error', false); finishTest(pExt.id); };
87
- const timerExt = os.setTimeout(() => { if (!extClose)
88
- assert('extended frame timeout', true); finishTest(pExt.id); }, 10000);
89
- await pExt.promise;
90
- os.clearTimeout(timerExt);
91
- // ── large message ──
92
- t.section('large message (>125 bytes)');
93
- const p2 = waitForTest();
94
- const ws2 = new WebSocket("ws://localhost:18923");
95
- let largeOk = false, closed2 = false;
96
- const largeStr = "A".repeat(1000);
97
- ws2.onopen = () => { ws2.send(largeStr); };
98
- ws2.onmessage = (e) => { largeOk = e.data.length === 1000; ws2.close(1000, "done"); };
99
- ws2.onclose = () => { closed2 = true; assert('large msg echo 1000 bytes', largeOk); finishTest(p2.id); };
100
- ws2.onerror = () => { assert('no error', false); finishTest(p2.id); };
101
- const timer2 = os.setTimeout(() => { if (!closed2) {
102
- assert('large msg no timeout', false);
103
- finishTest(p2.id);
104
- } }, 15000);
105
- await p2.promise;
106
- os.clearTimeout(timer2);
107
- // ── multiple messages ──
108
- t.section('multiple messages');
109
- const p3 = waitForTest();
110
- const ws3 = new WebSocket("ws://localhost:18923");
111
- let count = 0, closed3 = false;
112
- ws3.onopen = () => { ws3.send("a"); ws3.send("b"); ws3.send("c"); };
113
- ws3.onmessage = () => { count++; if (count === 3)
114
- ws3.close(1000, "done"); };
115
- ws3.onclose = () => { closed3 = true; assert('received 3 messages', count === 3); finishTest(p3.id); };
116
- ws3.onerror = () => { assert('no error', false); finishTest(p3.id); };
117
- const timer3 = os.setTimeout(() => { if (!closed3) {
118
- assert('no timeout', false);
119
- finishTest(p3.id);
120
- } }, 15000);
121
- await p3.promise;
122
- os.clearTimeout(timer3);
123
- // ── invalid URL ──
124
- t.section('invalid URL');
125
- const p4 = waitForTest();
126
- const ws4 = new WebSocket("not-a-websocket");
127
- let err4 = false;
128
- ws4.onerror = () => { err4 = true; };
129
- ws4.onclose = () => { assert('onerror fired', err4); finishTest(p4.id); };
130
- const timer4 = os.setTimeout(() => { assert('invalid url error', err4); finishTest(p4.id); }, 3000);
131
- await p4.promise;
132
- os.clearTimeout(timer4);
133
- // ── invalid scheme ──
134
- t.section('invalid scheme (http://)');
135
- const p5 = waitForTest();
136
- const ws5 = new WebSocket("http://example.com/ws");
137
- let err5 = false;
138
- ws5.onerror = () => { err5 = true; };
139
- ws5.onclose = () => { assert('http scheme onerror', err5); finishTest(p5.id); };
140
- const timer5 = os.setTimeout(() => { assert('http scheme error guard', err5); finishTest(p5.id); }, 3000);
141
- await p5.promise;
142
- os.clearTimeout(timer5);
143
- // ── early send/close ──
144
- t.section('send/close before onopen');
145
- const p6 = waitForTest();
146
- const ws6 = new WebSocket("ws://localhost:18923");
147
- ws6.send("noop");
148
- ws6.close(1000, "early");
149
- let close6 = false;
150
- ws6.onopen = () => { assert('onopen after early close', false); };
151
- ws6.onclose = () => { close6 = true; assert('early close ok', true); finishTest(p6.id); };
152
- ws6.onerror = () => { };
153
- const timer6 = os.setTimeout(() => { if (!close6)
154
- assert('no crash', true); finishTest(p6.id); }, 5000);
155
- await p6.promise;
156
- os.clearTimeout(timer6);
157
- // ── WSS (Secure WebSocket) ──
158
- t.section('wss basic text message');
159
- const p7 = waitForTest();
160
- let opened7 = false, received7 = false, closed7 = false;
161
- const ws7 = new WebSocket("wss://localhost:18924", { rejectUnauthorized: false });
162
- ws7.onopen = () => { opened7 = true; ws7.send("Hello WSS!"); };
163
- ws7.onmessage = () => { received7 = true; ws7.close(1000, "done"); };
164
- ws7.onclose = (e) => {
165
- closed7 = true;
166
- assert('wss onopen fired', opened7);
167
- assert('wss onmessage received', received7);
168
- assert('wss close code=1000', e.code === 1000);
169
- assert('wss wasClean', e.wasClean);
170
- finishTest(p7.id);
171
- };
172
- ws7.onerror = () => { assert('wss no error', false); finishTest(p7.id); };
173
- const timer7 = os.setTimeout(() => { if (!closed7) {
174
- assert('wss no timeout', false);
175
- finishTest(p7.id);
176
- } }, 10000);
177
- await p7.promise;
178
- os.clearTimeout(timer7);
179
- }
180
- };