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.
- package/README.en.md +204 -204
- package/README.md +203 -203
- package/examples/PdfCanvas.tsx +83 -83
- package/examples/exec_server.ts +35 -35
- package/examples/pdf_preview2.ts +375 -375
- package/examples/pdf_viewer.tsx +279 -279
- package/examples/test_gallery.tsx +285 -285
- package/examples/test_react_lazy.tsx +63 -63
- package/examples/test_react_listbox.tsx +71 -71
- package/examples/test_root_window.tsx +41 -41
- package/examples/test_tab.tsx +75 -75
- package/examples/tray_demo.tsx +81 -81
- package/lib/assert.ts +6 -6
- package/lib/cache-utils.ts +40 -40
- package/lib/dumpRects.ts +25 -25
- package/lib/fetch.ts +502 -502
- package/lib/http-common.ts +399 -399
- package/lib/http-server.ts +424 -424
- package/lib/polyfill.ts +171 -171
- package/lib/react-qw/components/index.ts +17 -17
- package/lib/react-qw/index.ts +90 -90
- package/lib/react-qw/jsx.d.ts +34 -34
- package/lib/react-qw/layout.ts +111 -111
- package/lib/react-qw/props.ts +36 -36
- package/lib/react-qw/reconciler.ts +373 -373
- package/lib/stream.ts +21 -21
- package/lib/text-codec.ts +171 -171
- package/lib/text-measure.ts +54 -54
- package/lib/url.ts +201 -201
- package/lib/vendor/web-streams/ponyfill.d.mts +829 -829
- package/lib/vendor/web-streams/ponyfill.mjs +8 -8
- package/lib/websocket.js +8 -0
- package/lib/websocket.ts +563 -555
- package/package.json +50 -44
- package/qwin-nowasm-x86.exe +0 -0
- package/qwin-nowasm.exe +0 -0
- package/qwin-x86.exe +0 -0
- package/qwin.exe +0 -0
- package/test/run.ts +96 -96
- package/test/test_basic.ts +9 -9
- package/test/test_basic_fetch.ts +33 -33
- package/test/test_brotli.ts +52 -52
- package/test/test_fetch_cache.ts +187 -187
- package/test/test_ffi.ts +174 -174
- package/test/test_frame_encoding.ts +132 -132
- package/test/test_helper.ts +80 -80
- package/test/test_http_import.ts +74 -74
- package/test/test_http_server.ts +136 -136
- package/test/test_import_dir.ts +5 -5
- package/test/test_ipv6.ts +129 -129
- package/test/test_meta_url.ts +4 -4
- package/test/test_mupdf_render.ts +74 -74
- package/test/test_mupdf_twice.ts +81 -81
- package/test/test_mupdf_wasm.ts +30 -30
- package/test/test_net_event.ts +55 -55
- package/test/test_net_fetch.ts +238 -238
- package/test/test_net_websocket.ts +164 -164
- package/test/test_polyfill.ts +159 -159
- package/test/test_url.ts +183 -183
- package/test/test_wasm_basic.ts +73 -73
- package/test/test_wasm_import_global.ts +45 -45
- package/test/test_wasm_sjlj.ts +125 -125
- package/test/test_wasm_types.ts +124 -124
- package/test/test_worker.ts +24 -24
- package/test/test_worker_fetch.ts +27 -27
- package/test/test_worker_net.ts +54 -54
- package/test/test_worker_wasm_concurrent.ts +45 -45
- package/test/worker_fetch.ts +13 -13
- package/test/worker_helper.ts +1 -1
- package/test/worker_http.ts +20 -20
- package/test/worker_import_lib.ts +13 -13
- package/test/worker_module.ts +17 -17
- package/test/worker_wasm_concurrent.ts +22 -22
- package/tsconfig.json +27 -27
- package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -571
- package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -2749
- package/vendor/mupdf-wasm/mupdf.d.ts +939 -939
- 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
|
+
}
|
package/test/test_polyfill.ts
CHANGED
|
@@ -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
|
+
}
|