quickwin 1.0.2 → 1.1.1

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 (78) hide show
  1. package/README.en.md +204 -204
  2. package/README.md +203 -203
  3. package/examples/PdfCanvas.tsx +83 -83
  4. package/examples/exec_server.ts +35 -35
  5. package/examples/pdf_preview2.ts +375 -375
  6. package/examples/pdf_viewer.tsx +279 -279
  7. package/examples/test_gallery.tsx +285 -285
  8. package/examples/test_react_lazy.tsx +63 -63
  9. package/examples/test_react_listbox.tsx +71 -71
  10. package/examples/test_root_window.tsx +41 -41
  11. package/examples/test_tab.tsx +75 -75
  12. package/examples/tray_demo.tsx +81 -81
  13. package/lib/assert.ts +6 -6
  14. package/lib/cache-utils.ts +40 -40
  15. package/lib/dumpRects.ts +25 -25
  16. package/lib/fetch.ts +502 -502
  17. package/lib/http-common.ts +399 -399
  18. package/lib/http-server.ts +424 -424
  19. package/lib/polyfill.ts +171 -171
  20. package/lib/react-qw/components/index.ts +17 -17
  21. package/lib/react-qw/index.ts +90 -90
  22. package/lib/react-qw/jsx.d.ts +34 -34
  23. package/lib/react-qw/layout.ts +111 -111
  24. package/lib/react-qw/props.ts +36 -36
  25. package/lib/react-qw/reconciler.ts +373 -373
  26. package/lib/stream.ts +21 -21
  27. package/lib/text-codec.ts +171 -171
  28. package/lib/text-measure.ts +54 -54
  29. package/lib/url.ts +201 -201
  30. package/lib/vendor/web-streams/ponyfill.d.mts +829 -829
  31. package/lib/vendor/web-streams/ponyfill.mjs +8 -8
  32. package/lib/websocket.js +8 -0
  33. package/lib/websocket.ts +563 -555
  34. package/package.json +50 -44
  35. package/qwin-nowasm-x86.exe +0 -0
  36. package/qwin-nowasm.exe +0 -0
  37. package/qwin-x86.exe +0 -0
  38. package/qwin.exe +0 -0
  39. package/test/run.ts +96 -96
  40. package/test/test_basic.ts +9 -9
  41. package/test/test_basic_fetch.ts +33 -33
  42. package/test/test_brotli.ts +52 -52
  43. package/test/test_fetch_cache.ts +187 -187
  44. package/test/test_ffi.ts +174 -174
  45. package/test/test_frame_encoding.ts +132 -132
  46. package/test/test_helper.ts +80 -80
  47. package/test/test_http_import.ts +74 -74
  48. package/test/test_http_server.ts +136 -136
  49. package/test/test_import_dir.ts +5 -5
  50. package/test/test_ipv6.ts +129 -129
  51. package/test/test_meta_url.ts +4 -4
  52. package/test/test_mupdf_render.ts +74 -74
  53. package/test/test_mupdf_twice.ts +81 -81
  54. package/test/test_mupdf_wasm.ts +30 -30
  55. package/test/test_net_event.ts +55 -55
  56. package/test/test_net_fetch.ts +238 -238
  57. package/test/test_net_websocket.ts +164 -164
  58. package/test/test_polyfill.ts +159 -159
  59. package/test/test_url.ts +183 -183
  60. package/test/test_wasm_basic.ts +73 -73
  61. package/test/test_wasm_import_global.ts +45 -45
  62. package/test/test_wasm_sjlj.ts +125 -125
  63. package/test/test_wasm_types.ts +124 -124
  64. package/test/test_worker.ts +24 -24
  65. package/test/test_worker_fetch.ts +27 -27
  66. package/test/test_worker_net.ts +54 -54
  67. package/test/test_worker_wasm_concurrent.ts +45 -45
  68. package/test/worker_fetch.ts +13 -13
  69. package/test/worker_helper.ts +1 -1
  70. package/test/worker_http.ts +20 -20
  71. package/test/worker_import_lib.ts +13 -13
  72. package/test/worker_module.ts +17 -17
  73. package/test/worker_wasm_concurrent.ts +22 -22
  74. package/tsconfig.json +27 -27
  75. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -571
  76. package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -2749
  77. package/vendor/mupdf-wasm/mupdf.d.ts +939 -939
  78. package/vendor/mupdf-wasm/mupdf.js +3317 -3317
@@ -1,164 +1,164 @@
1
- import '../lib/websocket.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-websocket',
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
- let testId = 0
15
- const resolvers: Record<number, () => void> = {}
16
-
17
- function waitForTest(): { promise: Promise<void>; id: number } {
18
- const id = ++testId
19
- const promise = new Promise<void>((resolve) => { resolvers[id] = resolve })
20
- return { promise, id }
21
- }
22
-
23
- function finishTest(id: number) {
24
- const r = resolvers[id]
25
- if (r) { delete resolvers[id]; r() }
26
- }
27
-
28
- // ── wss basic ──
29
- t.section('wss basic text message')
30
- const p1 = waitForTest()
31
- let opened = false, received = false, closed = false
32
- const ws1 = new WebSocket("ws://localhost:18923")
33
- ws1.onopen = () => { opened = true; ws1.send("Hello WebSocket!") }
34
- ws1.onmessage = () => { received = true; ws1.close(1000, "done") }
35
- ws1.onclose = (e) => {
36
- closed = true
37
- assert('onopen fired', opened)
38
- assert('onmessage received', received)
39
- assert('close code=1000', e.code === 1000)
40
- assert('wasClean', e.wasClean)
41
- finishTest(p1.id)
42
- }
43
- ws1.onerror = () => { assert('no error', false); finishTest(p1.id) }
44
- const timer1 = os.setTimeout(() => { if (!closed) { assert('no timeout', false); finishTest(p1.id) } }, 10000)
45
- await p1.promise
46
- os.clearTimeout(timer1)
47
-
48
- // ── binary send ──
49
- t.section('binary message (ArrayBuffer)')
50
- const pBin = waitForTest()
51
- const wsBin = new WebSocket("ws://localhost:18923")
52
- let binClose = false
53
- wsBin.onopen = () => {
54
- const buf = new ArrayBuffer(5)
55
- const v = new Uint8Array(buf)
56
- v[0] = 0x48; v[1] = 0x65; v[2] = 0x6C; v[3] = 0x6C; v[4] = 0x6F
57
- wsBin.send(buf)
58
- wsBin.send("binary-send-check")
59
- }
60
- let binMsgCount = 0
61
- wsBin.onmessage = () => { binMsgCount++; if (binMsgCount >= 2) wsBin.close(1000, "done") }
62
- wsBin.onclose = () => { binClose = true; assert('binary send completed', true); finishTest(pBin.id) }
63
- wsBin.onerror = () => { assert('no error', false); finishTest(pBin.id) }
64
- const timerBin = os.setTimeout(() => { if (!binClose) assert('binary no crash (timeout)', true); finishTest(pBin.id) }, 10000)
65
- await pBin.promise
66
- os.clearTimeout(timerBin)
67
-
68
- // ── extended length frame ──
69
- t.section('extended length frame (>65535 bytes)')
70
- const pExt = waitForTest()
71
- const wsExt = new WebSocket("ws://localhost:18923")
72
- let extClose = false
73
- wsExt.onopen = () => { wsExt.send("X".repeat(66000)) }
74
- wsExt.onmessage = () => { wsExt.close(1000, "done") }
75
- wsExt.onclose = () => { extClose = true; assert('extended frame sent', true); finishTest(pExt.id) }
76
- wsExt.onerror = () => { assert('no error', false); finishTest(pExt.id) }
77
- const timerExt = os.setTimeout(() => { if (!extClose) assert('extended frame timeout', true); finishTest(pExt.id) }, 10000)
78
- await pExt.promise
79
- os.clearTimeout(timerExt)
80
-
81
- // ── large message ──
82
- t.section('large message (>125 bytes)')
83
- const p2 = waitForTest()
84
- const ws2 = new WebSocket("ws://localhost:18923")
85
- let largeOk = false, closed2 = false
86
- const largeStr = "A".repeat(1000)
87
- ws2.onopen = () => { ws2.send(largeStr) }
88
- ws2.onmessage = (e) => { largeOk = (e.data as string).length === 1000; ws2.close(1000, "done") }
89
- ws2.onclose = () => { closed2 = true; assert('large msg echo 1000 bytes', largeOk); finishTest(p2.id) }
90
- ws2.onerror = () => { assert('no error', false); finishTest(p2.id) }
91
- const timer2 = os.setTimeout(() => { if (!closed2) { assert('large msg no timeout', false); finishTest(p2.id) } }, 15000)
92
- await p2.promise
93
- os.clearTimeout(timer2)
94
-
95
- // ── multiple messages ──
96
- t.section('multiple messages')
97
- const p3 = waitForTest()
98
- const ws3 = new WebSocket("ws://localhost:18923")
99
- let count = 0, closed3 = false
100
- ws3.onopen = () => { ws3.send("a"); ws3.send("b"); ws3.send("c") }
101
- ws3.onmessage = () => { count++; if (count === 3) ws3.close(1000, "done") }
102
- ws3.onclose = () => { closed3 = true; assert('received 3 messages', count === 3); finishTest(p3.id) }
103
- ws3.onerror = () => { assert('no error', false); finishTest(p3.id) }
104
- const timer3 = os.setTimeout(() => { if (!closed3) { assert('no timeout', false); finishTest(p3.id) } }, 15000)
105
- await p3.promise
106
- os.clearTimeout(timer3)
107
-
108
- // ── invalid URL ──
109
- t.section('invalid URL')
110
- const p4 = waitForTest()
111
- const ws4 = new WebSocket("not-a-websocket")
112
- let err4 = false
113
- ws4.onerror = () => { err4 = true }
114
- ws4.onclose = () => { assert('onerror fired', err4); finishTest(p4.id) }
115
- const timer4 = os.setTimeout(() => { assert('invalid url error', err4); finishTest(p4.id) }, 3000)
116
- await p4.promise
117
- os.clearTimeout(timer4)
118
-
119
- // ── invalid scheme ──
120
- t.section('invalid scheme (http://)')
121
- const p5 = waitForTest()
122
- const ws5 = new WebSocket("http://example.com/ws")
123
- let err5 = false
124
- ws5.onerror = () => { err5 = true }
125
- ws5.onclose = () => { assert('http scheme onerror', err5); finishTest(p5.id) }
126
- const timer5 = os.setTimeout(() => { assert('http scheme error guard', err5); finishTest(p5.id) }, 3000)
127
- await p5.promise
128
- os.clearTimeout(timer5)
129
-
130
- // ── early send/close ──
131
- t.section('send/close before onopen')
132
- const p6 = waitForTest()
133
- const ws6 = new WebSocket("ws://localhost:18923")
134
- ws6.send("noop")
135
- ws6.close(1000, "early")
136
- let close6 = false
137
- ws6.onopen = () => { assert('onopen after early close', false) }
138
- ws6.onclose = () => { close6 = true; assert('early close ok', true); finishTest(p6.id) }
139
- ws6.onerror = () => {}
140
- const timer6 = os.setTimeout(() => { if (!close6) assert('no crash', true); finishTest(p6.id) }, 5000)
141
- await p6.promise
142
- os.clearTimeout(timer6)
143
-
144
- // ── WSS (Secure WebSocket) ──
145
- t.section('wss basic text message')
146
- const p7 = waitForTest()
147
- let opened7 = false, received7 = false, closed7 = false
148
- const ws7 = new WebSocket("wss://localhost:18924")
149
- ws7.onopen = () => { opened7 = true; ws7.send("Hello WSS!") }
150
- ws7.onmessage = () => { received7 = true; ws7.close(1000, "done") }
151
- ws7.onclose = (e) => {
152
- closed7 = true
153
- assert('wss onopen fired', opened7)
154
- assert('wss onmessage received', received7)
155
- assert('wss close code=1000', e.code === 1000)
156
- assert('wss wasClean', e.wasClean)
157
- finishTest(p7.id)
158
- }
159
- ws7.onerror = () => { assert('wss no error', false); finishTest(p7.id) }
160
- const timer7 = os.setTimeout(() => { if (!closed7) { assert('wss no timeout', false); finishTest(p7.id) } }, 10000)
161
- await p7.promise
162
- os.clearTimeout(timer7)
163
- }
164
- }
1
+ import '../lib/websocket.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-websocket',
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
+ let testId = 0
15
+ const resolvers: Record<number, () => void> = {}
16
+
17
+ function waitForTest(): { promise: Promise<void>; id: number } {
18
+ const id = ++testId
19
+ const promise = new Promise<void>((resolve) => { resolvers[id] = resolve })
20
+ return { promise, id }
21
+ }
22
+
23
+ function finishTest(id: number) {
24
+ const r = resolvers[id]
25
+ if (r) { delete resolvers[id]; r() }
26
+ }
27
+
28
+ // ── wss basic ──
29
+ t.section('wss basic text message')
30
+ const p1 = waitForTest()
31
+ let opened = false, received = false, closed = false
32
+ const ws1 = new WebSocket("ws://localhost:18923")
33
+ ws1.onopen = () => { opened = true; ws1.send("Hello WebSocket!") }
34
+ ws1.onmessage = () => { received = true; ws1.close(1000, "done") }
35
+ ws1.onclose = (e) => {
36
+ closed = true
37
+ assert('onopen fired', opened)
38
+ assert('onmessage received', received)
39
+ assert('close code=1000', e.code === 1000)
40
+ assert('wasClean', e.wasClean)
41
+ finishTest(p1.id)
42
+ }
43
+ ws1.onerror = () => { assert('no error', false); finishTest(p1.id) }
44
+ const timer1 = os.setTimeout(() => { if (!closed) { assert('no timeout', false); finishTest(p1.id) } }, 10000)
45
+ await p1.promise
46
+ os.clearTimeout(timer1)
47
+
48
+ // ── binary send ──
49
+ t.section('binary message (ArrayBuffer)')
50
+ const pBin = waitForTest()
51
+ const wsBin = new WebSocket("ws://localhost:18923")
52
+ let binClose = false
53
+ wsBin.onopen = () => {
54
+ const buf = new ArrayBuffer(5)
55
+ const v = new Uint8Array(buf)
56
+ v[0] = 0x48; v[1] = 0x65; v[2] = 0x6C; v[3] = 0x6C; v[4] = 0x6F
57
+ wsBin.send(buf)
58
+ wsBin.send("binary-send-check")
59
+ }
60
+ let binMsgCount = 0
61
+ wsBin.onmessage = () => { binMsgCount++; if (binMsgCount >= 2) wsBin.close(1000, "done") }
62
+ wsBin.onclose = () => { binClose = true; assert('binary send completed', true); finishTest(pBin.id) }
63
+ wsBin.onerror = () => { assert('no error', false); finishTest(pBin.id) }
64
+ const timerBin = os.setTimeout(() => { if (!binClose) assert('binary no crash (timeout)', true); finishTest(pBin.id) }, 10000)
65
+ await pBin.promise
66
+ os.clearTimeout(timerBin)
67
+
68
+ // ── extended length frame ──
69
+ t.section('extended length frame (>65535 bytes)')
70
+ const pExt = waitForTest()
71
+ const wsExt = new WebSocket("ws://localhost:18923")
72
+ let extClose = false
73
+ wsExt.onopen = () => { wsExt.send("X".repeat(66000)) }
74
+ wsExt.onmessage = () => { wsExt.close(1000, "done") }
75
+ wsExt.onclose = () => { extClose = true; assert('extended frame sent', true); finishTest(pExt.id) }
76
+ wsExt.onerror = () => { assert('no error', false); finishTest(pExt.id) }
77
+ const timerExt = os.setTimeout(() => { if (!extClose) assert('extended frame timeout', true); finishTest(pExt.id) }, 10000)
78
+ await pExt.promise
79
+ os.clearTimeout(timerExt)
80
+
81
+ // ── large message ──
82
+ t.section('large message (>125 bytes)')
83
+ const p2 = waitForTest()
84
+ const ws2 = new WebSocket("ws://localhost:18923")
85
+ let largeOk = false, closed2 = false
86
+ const largeStr = "A".repeat(1000)
87
+ ws2.onopen = () => { ws2.send(largeStr) }
88
+ ws2.onmessage = (e) => { largeOk = (e.data as string).length === 1000; ws2.close(1000, "done") }
89
+ ws2.onclose = () => { closed2 = true; assert('large msg echo 1000 bytes', largeOk); finishTest(p2.id) }
90
+ ws2.onerror = () => { assert('no error', false); finishTest(p2.id) }
91
+ const timer2 = os.setTimeout(() => { if (!closed2) { assert('large msg no timeout', false); finishTest(p2.id) } }, 15000)
92
+ await p2.promise
93
+ os.clearTimeout(timer2)
94
+
95
+ // ── multiple messages ──
96
+ t.section('multiple messages')
97
+ const p3 = waitForTest()
98
+ const ws3 = new WebSocket("ws://localhost:18923")
99
+ let count = 0, closed3 = false
100
+ ws3.onopen = () => { ws3.send("a"); ws3.send("b"); ws3.send("c") }
101
+ ws3.onmessage = () => { count++; if (count === 3) ws3.close(1000, "done") }
102
+ ws3.onclose = () => { closed3 = true; assert('received 3 messages', count === 3); finishTest(p3.id) }
103
+ ws3.onerror = () => { assert('no error', false); finishTest(p3.id) }
104
+ const timer3 = os.setTimeout(() => { if (!closed3) { assert('no timeout', false); finishTest(p3.id) } }, 15000)
105
+ await p3.promise
106
+ os.clearTimeout(timer3)
107
+
108
+ // ── invalid URL ──
109
+ t.section('invalid URL')
110
+ const p4 = waitForTest()
111
+ const ws4 = new WebSocket("not-a-websocket")
112
+ let err4 = false
113
+ ws4.onerror = () => { err4 = true }
114
+ ws4.onclose = () => { assert('onerror fired', err4); finishTest(p4.id) }
115
+ const timer4 = os.setTimeout(() => { assert('invalid url error', err4); finishTest(p4.id) }, 3000)
116
+ await p4.promise
117
+ os.clearTimeout(timer4)
118
+
119
+ // ── invalid scheme ──
120
+ t.section('invalid scheme (http://)')
121
+ const p5 = waitForTest()
122
+ const ws5 = new WebSocket("http://example.com/ws")
123
+ let err5 = false
124
+ ws5.onerror = () => { err5 = true }
125
+ ws5.onclose = () => { assert('http scheme onerror', err5); finishTest(p5.id) }
126
+ const timer5 = os.setTimeout(() => { assert('http scheme error guard', err5); finishTest(p5.id) }, 3000)
127
+ await p5.promise
128
+ os.clearTimeout(timer5)
129
+
130
+ // ── early send/close ──
131
+ t.section('send/close before onopen')
132
+ const p6 = waitForTest()
133
+ const ws6 = new WebSocket("ws://localhost:18923")
134
+ ws6.send("noop")
135
+ ws6.close(1000, "early")
136
+ let close6 = false
137
+ ws6.onopen = () => { assert('onopen after early close', false) }
138
+ ws6.onclose = () => { close6 = true; assert('early close ok', true); finishTest(p6.id) }
139
+ ws6.onerror = () => {}
140
+ const timer6 = os.setTimeout(() => { if (!close6) assert('no crash', true); finishTest(p6.id) }, 5000)
141
+ await p6.promise
142
+ os.clearTimeout(timer6)
143
+
144
+ // ── WSS (Secure WebSocket) ──
145
+ t.section('wss basic text message')
146
+ const p7 = waitForTest()
147
+ let opened7 = false, received7 = false, closed7 = false
148
+ const ws7 = new WebSocket("wss://localhost:18924")
149
+ ws7.onopen = () => { opened7 = true; ws7.send("Hello WSS!") }
150
+ ws7.onmessage = () => { received7 = true; ws7.close(1000, "done") }
151
+ ws7.onclose = (e) => {
152
+ closed7 = true
153
+ assert('wss onopen fired', opened7)
154
+ assert('wss onmessage received', received7)
155
+ assert('wss close code=1000', e.code === 1000)
156
+ assert('wss wasClean', e.wasClean)
157
+ finishTest(p7.id)
158
+ }
159
+ ws7.onerror = () => { assert('wss no error', false); finishTest(p7.id) }
160
+ const timer7 = os.setTimeout(() => { if (!closed7) { assert('wss no timeout', false); finishTest(p7.id) } }, 10000)
161
+ await p7.promise
162
+ os.clearTimeout(timer7)
163
+ }
164
+ }
@@ -1,159 +1,159 @@
1
- import '../lib/polyfill.js'
2
- import * as std from 'std'
3
- import { Tester } from './test_helper.js'
4
-
5
- function assert(t: Tester, name: string, ok: boolean): void {
6
- if (ok) { t.ok++; std.printf(' PASS: %s\n', name) }
7
- else { t.fail++; std.printf(' FAIL: %s\n', name) }
8
- }
9
-
10
- export const suite = {
11
- name: 'polyfill',
12
- run: async (t: Tester) => {
13
- t.section('btoa/atob')
14
- const orig = "Hello\u0000World"
15
- const b64 = btoa(orig)
16
- const back = atob(b64)
17
- assert(t, 'btoa/atob roundtrip', back === orig)
18
- assert(t, 'btoa produces base64 chars', /^[A-Za-z0-9+/=]+$/.test(b64))
19
-
20
- const hash = new Uint8Array([0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12])
21
- assert(t, 'btoa(SHA-1 sample) matches known value', btoa(String.fromCharCode(...hash)) === "L9ThxnotKPzthJ7hu3bnORuT6xI=")
22
-
23
- t.section('crypto.getRandomValues')
24
- const buf = new Uint8Array(32)
25
- crypto.getRandomValues(buf)
26
- assert(t, 'length=32', buf.length === 32)
27
- assert(t, 'not all zeros', buf.some(b => b !== 0))
28
-
29
- t.section('crypto.subtle.digest SHA-1')
30
- const data = new TextEncoder().encode("Hello")
31
- const hashBuf = await crypto.subtle.digest("SHA-1", data)
32
- const hb = new Uint8Array(hashBuf)
33
- assert(t, 'output length=20', hb.length === 20)
34
- assert(t, 'SHA-1(\'Hello\') matches', hb[0] === 0xF7 && hb[1] === 0xFF && hb[19] === 0xF0)
35
-
36
- t.section('TextEncoder utf-8')
37
- const enc = new TextEncoder()
38
- assert(t, 'default encoding', enc.encoding === 'utf-8')
39
- const encoded = enc.encode("Hello")
40
- assert(t, 'encode length', encoded.length === 5)
41
- assert(t, 'encode bytes', encoded[0] === 0x48 && encoded[4] === 0x6F)
42
- assert(t, 'encoding property', enc.encoding === 'utf-8')
43
-
44
- const enc2 = new TextEncoder('utf-8')
45
- assert(t, 'utf-8 label', enc2.encoding === 'utf-8')
46
-
47
- const enc3 = new TextEncoder('utf8')
48
- assert(t, 'utf8 alias', enc3.encoding === 'utf-8')
49
-
50
- let threw = false
51
- try { new TextEncoder('gbk' as any) } catch (e) { threw = e instanceof RangeError }
52
- assert(t, 'unsupported label throws RangeError', threw)
53
-
54
- t.section('TextEncoder utf-16le')
55
- const enc16le = new TextEncoder('utf-16le')
56
- assert(t, 'encoding property', enc16le.encoding === 'utf-16le')
57
-
58
- const hello16le = enc16le.encode('AB')
59
- assert(t, 'length = 4', hello16le.length === 4)
60
- assert(t, 'A = 0x41 0x00', hello16le[0] === 0x41 && hello16le[1] === 0x00)
61
- assert(t, 'B = 0x42 0x00', hello16le[2] === 0x42 && hello16le[3] === 0x00)
62
-
63
- const null16le = enc16le.encode('\0')
64
- assert(t, 'null char = 0x00 0x00', null16le.length === 2 && null16le[0] === 0 && null16le[1] === 0)
65
-
66
- const chinese16le = enc16le.encode('\u4e2d') // 中 = U+4E2D
67
- assert(t, '中 = 0x2D 0x4E', chinese16le[0] === 0x2D && chinese16le[1] === 0x4E)
68
-
69
- t.section('TextEncoder utf-16be')
70
- const enc16be = new TextEncoder('utf-16be')
71
- assert(t, 'encoding property', enc16be.encoding === 'utf-16be')
72
-
73
- const hello16be = enc16be.encode('AB')
74
- assert(t, 'length = 4', hello16be.length === 4)
75
- assert(t, 'A = 0x00 0x41', hello16be[0] === 0x00 && hello16be[1] === 0x41)
76
- assert(t, 'B = 0x00 0x42', hello16be[2] === 0x00 && hello16be[3] === 0x42)
77
-
78
- t.section('TextDecoder utf-8')
79
- const dec = new TextDecoder()
80
- assert(t, 'default encoding', dec.encoding === 'utf-8')
81
- assert(t, 'default fatal', dec.fatal === false)
82
- assert(t, 'default ignoreBOM', dec.ignoreBOM === false)
83
- assert(t, 'decode utf-8 bytes', dec.decode(encoded) === 'Hello')
84
- assert(t, 'decode empty', dec.decode() === '')
85
- assert(t, 'decode null', dec.decode(null) === '')
86
-
87
- const decFatal = new TextDecoder('utf-8', { fatal: true })
88
- assert(t, 'fatal encoding', decFatal.encoding === 'utf-8')
89
- assert(t, 'fatal true', decFatal.fatal === true)
90
-
91
- let threw2 = false
92
- try { decFatal.decode(new Uint8Array([0xFF])) } catch (e) { threw2 = e instanceof TypeError }
93
- assert(t, 'fatal decode throws TypeError', threw2)
94
-
95
- const nonFatal = new TextDecoder()
96
- const replaced = nonFatal.decode(new Uint8Array([0xFF]))
97
- assert(t, 'non-fatal replaces with U+FFFD', replaced === '\uFFFD')
98
-
99
- t.section('TextDecoder utf-16le')
100
- const dec16le = new TextDecoder('utf-16le')
101
- assert(t, 'encoding = utf-16le', dec16le.encoding === 'utf-16le')
102
-
103
- const ab = new Uint8Array([0x41, 0x00, 0x42, 0x00])
104
- assert(t, 'AB from utf-16le', dec16le.decode(ab) === 'AB')
105
-
106
- const chn = new Uint8Array([0x2D, 0x4E])
107
- assert(t, '中 from utf-16le', dec16le.decode(chn) === '\u4e2d')
108
-
109
- const withBom = new Uint8Array([0xFF, 0xFE, 0x41, 0x00])
110
- assert(t, 'BOM stripped by default', dec16le.decode(withBom) === 'A')
111
-
112
- const dec16leNoBom = new TextDecoder('utf-16le', { ignoreBOM: true })
113
- assert(t, 'ignoreBOM keeps BOM', dec16leNoBom.decode(withBom) === '\uFEFFA')
114
-
115
- t.section('TextDecoder utf-16be')
116
- const dec16be = new TextDecoder('utf-16be')
117
- const be = new Uint8Array([0x00, 0x41, 0x00, 0x42])
118
- assert(t, 'AB from utf-16be', dec16be.decode(be) === 'AB')
119
-
120
- const chnBe = new Uint8Array([0x4E, 0x2D])
121
- assert(t, '中 from utf-16be', dec16be.decode(chnBe) === '\u4e2d')
122
-
123
- t.section('TextDecoder roundtrip')
124
- const texts = ['Hello', 'Hello 世界', '\u4e2d\u6587', 'A\0B', '\uD83D\uDE00'] // emoji
125
- for (const txt of texts) {
126
- const enc8 = new TextEncoder()
127
- const bytes = enc8.encode(txt)
128
- const dec8 = new TextDecoder()
129
- assert(t, `utf-8 roundtrip: ${txt}`, dec8.decode(bytes) === txt)
130
-
131
- const enc16le = new TextEncoder('utf-16le')
132
- const bytes16 = enc16le.encode(txt)
133
- const dec16le = new TextDecoder('utf-16le')
134
- assert(t, `utf-16le roundtrip: ${txt}`, dec16le.decode(bytes16) === txt)
135
-
136
- const enc16be = new TextEncoder('utf-16be')
137
- const bytes16be = enc16be.encode(txt)
138
- const dec16be = new TextDecoder('utf-16be')
139
- assert(t, `utf-16be roundtrip: ${txt}`, dec16be.decode(bytes16be) === txt)
140
- }
141
-
142
- t.section('SHA-1 + btoa (WebSocket accept key)')
143
- const MAGIC_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
144
- const testKey = 'dGhlIHNhbXBsZSBub25jZQ=='
145
- const concat = testKey + MAGIC_GUID
146
- const acceptBuf = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(concat))
147
- const acceptBytes = new Uint8Array(acceptBuf)
148
- const acceptB64 = btoa(String.fromCharCode(...acceptBytes))
149
- assert(t, 'RFC 6455 accept key matches', acceptB64 === "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=")
150
-
151
- let rejected = false
152
- try {
153
- await crypto.subtle.digest("SHA-256" as any, new Uint8Array(1))
154
- } catch {
155
- rejected = true
156
- }
157
- assert(t, 'unsupported algorithm rejected', rejected)
158
- }
159
- }
1
+ import '../lib/polyfill.js'
2
+ import * as std from 'std'
3
+ import { Tester } from './test_helper.js'
4
+
5
+ function assert(t: Tester, name: string, ok: boolean): void {
6
+ if (ok) { t.ok++; std.printf(' PASS: %s\n', name) }
7
+ else { t.fail++; std.printf(' FAIL: %s\n', name) }
8
+ }
9
+
10
+ export const suite = {
11
+ name: 'polyfill',
12
+ run: async (t: Tester) => {
13
+ t.section('btoa/atob')
14
+ const orig = "Hello\u0000World"
15
+ const b64 = btoa(orig)
16
+ const back = atob(b64)
17
+ assert(t, 'btoa/atob roundtrip', back === orig)
18
+ assert(t, 'btoa produces base64 chars', /^[A-Za-z0-9+/=]+$/.test(b64))
19
+
20
+ const hash = new Uint8Array([0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12])
21
+ assert(t, 'btoa(SHA-1 sample) matches known value', btoa(String.fromCharCode(...hash)) === "L9ThxnotKPzthJ7hu3bnORuT6xI=")
22
+
23
+ t.section('crypto.getRandomValues')
24
+ const buf = new Uint8Array(32)
25
+ crypto.getRandomValues(buf)
26
+ assert(t, 'length=32', buf.length === 32)
27
+ assert(t, 'not all zeros', buf.some(b => b !== 0))
28
+
29
+ t.section('crypto.subtle.digest SHA-1')
30
+ const data = new TextEncoder().encode("Hello")
31
+ const hashBuf = await crypto.subtle.digest("SHA-1", data)
32
+ const hb = new Uint8Array(hashBuf)
33
+ assert(t, 'output length=20', hb.length === 20)
34
+ assert(t, 'SHA-1(\'Hello\') matches', hb[0] === 0xF7 && hb[1] === 0xFF && hb[19] === 0xF0)
35
+
36
+ t.section('TextEncoder utf-8')
37
+ const enc = new TextEncoder()
38
+ assert(t, 'default encoding', enc.encoding === 'utf-8')
39
+ const encoded = enc.encode("Hello")
40
+ assert(t, 'encode length', encoded.length === 5)
41
+ assert(t, 'encode bytes', encoded[0] === 0x48 && encoded[4] === 0x6F)
42
+ assert(t, 'encoding property', enc.encoding === 'utf-8')
43
+
44
+ const enc2 = new TextEncoder('utf-8')
45
+ assert(t, 'utf-8 label', enc2.encoding === 'utf-8')
46
+
47
+ const enc3 = new TextEncoder('utf8')
48
+ assert(t, 'utf8 alias', enc3.encoding === 'utf-8')
49
+
50
+ let threw = false
51
+ try { new TextEncoder('gbk' as any) } catch (e) { threw = e instanceof RangeError }
52
+ assert(t, 'unsupported label throws RangeError', threw)
53
+
54
+ t.section('TextEncoder utf-16le')
55
+ const enc16le = new TextEncoder('utf-16le')
56
+ assert(t, 'encoding property', enc16le.encoding === 'utf-16le')
57
+
58
+ const hello16le = enc16le.encode('AB')
59
+ assert(t, 'length = 4', hello16le.length === 4)
60
+ assert(t, 'A = 0x41 0x00', hello16le[0] === 0x41 && hello16le[1] === 0x00)
61
+ assert(t, 'B = 0x42 0x00', hello16le[2] === 0x42 && hello16le[3] === 0x00)
62
+
63
+ const null16le = enc16le.encode('\0')
64
+ assert(t, 'null char = 0x00 0x00', null16le.length === 2 && null16le[0] === 0 && null16le[1] === 0)
65
+
66
+ const chinese16le = enc16le.encode('\u4e2d') // 中 = U+4E2D
67
+ assert(t, '中 = 0x2D 0x4E', chinese16le[0] === 0x2D && chinese16le[1] === 0x4E)
68
+
69
+ t.section('TextEncoder utf-16be')
70
+ const enc16be = new TextEncoder('utf-16be')
71
+ assert(t, 'encoding property', enc16be.encoding === 'utf-16be')
72
+
73
+ const hello16be = enc16be.encode('AB')
74
+ assert(t, 'length = 4', hello16be.length === 4)
75
+ assert(t, 'A = 0x00 0x41', hello16be[0] === 0x00 && hello16be[1] === 0x41)
76
+ assert(t, 'B = 0x00 0x42', hello16be[2] === 0x00 && hello16be[3] === 0x42)
77
+
78
+ t.section('TextDecoder utf-8')
79
+ const dec = new TextDecoder()
80
+ assert(t, 'default encoding', dec.encoding === 'utf-8')
81
+ assert(t, 'default fatal', dec.fatal === false)
82
+ assert(t, 'default ignoreBOM', dec.ignoreBOM === false)
83
+ assert(t, 'decode utf-8 bytes', dec.decode(encoded) === 'Hello')
84
+ assert(t, 'decode empty', dec.decode() === '')
85
+ assert(t, 'decode null', dec.decode(null) === '')
86
+
87
+ const decFatal = new TextDecoder('utf-8', { fatal: true })
88
+ assert(t, 'fatal encoding', decFatal.encoding === 'utf-8')
89
+ assert(t, 'fatal true', decFatal.fatal === true)
90
+
91
+ let threw2 = false
92
+ try { decFatal.decode(new Uint8Array([0xFF])) } catch (e) { threw2 = e instanceof TypeError }
93
+ assert(t, 'fatal decode throws TypeError', threw2)
94
+
95
+ const nonFatal = new TextDecoder()
96
+ const replaced = nonFatal.decode(new Uint8Array([0xFF]))
97
+ assert(t, 'non-fatal replaces with U+FFFD', replaced === '\uFFFD')
98
+
99
+ t.section('TextDecoder utf-16le')
100
+ const dec16le = new TextDecoder('utf-16le')
101
+ assert(t, 'encoding = utf-16le', dec16le.encoding === 'utf-16le')
102
+
103
+ const ab = new Uint8Array([0x41, 0x00, 0x42, 0x00])
104
+ assert(t, 'AB from utf-16le', dec16le.decode(ab) === 'AB')
105
+
106
+ const chn = new Uint8Array([0x2D, 0x4E])
107
+ assert(t, '中 from utf-16le', dec16le.decode(chn) === '\u4e2d')
108
+
109
+ const withBom = new Uint8Array([0xFF, 0xFE, 0x41, 0x00])
110
+ assert(t, 'BOM stripped by default', dec16le.decode(withBom) === 'A')
111
+
112
+ const dec16leNoBom = new TextDecoder('utf-16le', { ignoreBOM: true })
113
+ assert(t, 'ignoreBOM keeps BOM', dec16leNoBom.decode(withBom) === '\uFEFFA')
114
+
115
+ t.section('TextDecoder utf-16be')
116
+ const dec16be = new TextDecoder('utf-16be')
117
+ const be = new Uint8Array([0x00, 0x41, 0x00, 0x42])
118
+ assert(t, 'AB from utf-16be', dec16be.decode(be) === 'AB')
119
+
120
+ const chnBe = new Uint8Array([0x4E, 0x2D])
121
+ assert(t, '中 from utf-16be', dec16be.decode(chnBe) === '\u4e2d')
122
+
123
+ t.section('TextDecoder roundtrip')
124
+ const texts = ['Hello', 'Hello 世界', '\u4e2d\u6587', 'A\0B', '\uD83D\uDE00'] // emoji
125
+ for (const txt of texts) {
126
+ const enc8 = new TextEncoder()
127
+ const bytes = enc8.encode(txt)
128
+ const dec8 = new TextDecoder()
129
+ assert(t, `utf-8 roundtrip: ${txt}`, dec8.decode(bytes) === txt)
130
+
131
+ const enc16le = new TextEncoder('utf-16le')
132
+ const bytes16 = enc16le.encode(txt)
133
+ const dec16le = new TextDecoder('utf-16le')
134
+ assert(t, `utf-16le roundtrip: ${txt}`, dec16le.decode(bytes16) === txt)
135
+
136
+ const enc16be = new TextEncoder('utf-16be')
137
+ const bytes16be = enc16be.encode(txt)
138
+ const dec16be = new TextDecoder('utf-16be')
139
+ assert(t, `utf-16be roundtrip: ${txt}`, dec16be.decode(bytes16be) === txt)
140
+ }
141
+
142
+ t.section('SHA-1 + btoa (WebSocket accept key)')
143
+ const MAGIC_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
144
+ const testKey = 'dGhlIHNhbXBsZSBub25jZQ=='
145
+ const concat = testKey + MAGIC_GUID
146
+ const acceptBuf = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(concat))
147
+ const acceptBytes = new Uint8Array(acceptBuf)
148
+ const acceptB64 = btoa(String.fromCharCode(...acceptBytes))
149
+ assert(t, 'RFC 6455 accept key matches', acceptB64 === "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=")
150
+
151
+ let rejected = false
152
+ try {
153
+ await crypto.subtle.digest("SHA-256" as any, new Uint8Array(1))
154
+ } catch {
155
+ rejected = true
156
+ }
157
+ assert(t, 'unsupported algorithm rejected', rejected)
158
+ }
159
+ }