quickwin 2026.5.2-3.145209

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +6 -0
  2. package/examples/pdf_preview.js +440 -0
  3. package/examples/pdf_preview.ts +470 -0
  4. package/examples/preact_demo.js +35 -0
  5. package/examples/preact_demo.tsx +49 -0
  6. package/examples/tray_demo.js +75 -0
  7. package/examples/tray_demo.tsx +79 -0
  8. package/lib/fetch.js +746 -0
  9. package/lib/fetch.ts +811 -0
  10. package/lib/polyfill.js +500 -0
  11. package/lib/polyfill.ts +454 -0
  12. package/lib/preact/hooks.js +287 -0
  13. package/lib/preact/hooks.ts +330 -0
  14. package/lib/preact/jsx-runtime.js +1 -0
  15. package/lib/preact/jsx-runtime.ts +2 -0
  16. package/lib/preact/jsx.d.ts +36 -0
  17. package/lib/preact/layout.js +153 -0
  18. package/lib/preact/layout.ts +183 -0
  19. package/lib/preact/preact.js +54 -0
  20. package/lib/preact/preact.ts +133 -0
  21. package/lib/preact/props.js +99 -0
  22. package/lib/preact/props.ts +119 -0
  23. package/lib/preact/render.js +320 -0
  24. package/lib/preact/render.ts +353 -0
  25. package/lib/websocket.js +540 -0
  26. package/lib/websocket.ts +574 -0
  27. package/package.json +32 -0
  28. package/quickwin.d.ts +657 -0
  29. package/test/add.wasm +0 -0
  30. package/test/complex.wasm +0 -0
  31. package/test/complex_imports.wasm +0 -0
  32. package/test/global_imports.wasm +0 -0
  33. package/test/import_func.wasm +0 -0
  34. package/test/imports.wasm +0 -0
  35. package/test/run.js +86 -0
  36. package/test/run.ts +90 -0
  37. package/test/sjlj.wasm +0 -0
  38. package/test/test_basic.js +7 -0
  39. package/test/test_basic.ts +9 -0
  40. package/test/test_brotli.js +48 -0
  41. package/test/test_brotli.ts +52 -0
  42. package/test/test_fetch_cache.js +131 -0
  43. package/test/test_fetch_cache.ts +141 -0
  44. package/test/test_ffi.js +157 -0
  45. package/test/test_ffi.ts +174 -0
  46. package/test/test_frame_encoding.js +128 -0
  47. package/test/test_frame_encoding.ts +132 -0
  48. package/test/test_helper.js +84 -0
  49. package/test/test_helper.ts +80 -0
  50. package/test/test_http_import.js +78 -0
  51. package/test/test_http_import.ts +74 -0
  52. package/test/test_mupdf_render.js +69 -0
  53. package/test/test_mupdf_render.ts +74 -0
  54. package/test/test_mupdf_twice.js +77 -0
  55. package/test/test_mupdf_twice.ts +81 -0
  56. package/test/test_mupdf_wasm.js +33 -0
  57. package/test/test_mupdf_wasm.ts +30 -0
  58. package/test/test_net_event.js +63 -0
  59. package/test/test_net_event.ts +59 -0
  60. package/test/test_net_fetch.js +153 -0
  61. package/test/test_net_fetch.ts +131 -0
  62. package/test/test_net_websocket.js +158 -0
  63. package/test/test_net_websocket.ts +144 -0
  64. package/test/test_polyfill.js +58 -0
  65. package/test/test_polyfill.ts +60 -0
  66. package/test/test_url.js +173 -0
  67. package/test/test_url.ts +183 -0
  68. package/test/test_wasm_basic.js +82 -0
  69. package/test/test_wasm_basic.ts +70 -0
  70. package/test/test_wasm_import_global.js +41 -0
  71. package/test/test_wasm_import_global.ts +39 -0
  72. package/test/test_wasm_sjlj.js +153 -0
  73. package/test/test_wasm_sjlj.ts +134 -0
  74. package/test/test_wasm_types.js +96 -0
  75. package/test/test_wasm_types.ts +108 -0
  76. package/test/types.wasm +0 -0
  77. package/tsconfig.json +18 -0
  78. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -0
  79. package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -0
  80. package/vendor/mupdf-wasm/mupdf-wasm.wasm +0 -0
  81. package/vendor/mupdf-wasm/mupdf.d.ts +939 -0
  82. package/vendor/mupdf-wasm/mupdf.js +3317 -0
  83. package/win-mingw64.exe +0 -0
@@ -0,0 +1,58 @@
1
+ import '../lib/polyfill.js';
2
+ import * as std from 'std';
3
+ export const suite = {
4
+ name: 'polyfill',
5
+ run: async (t) => {
6
+ function assert(name, ok) {
7
+ if (ok) {
8
+ t.ok++;
9
+ std.printf(' PASS: %s\n', name);
10
+ }
11
+ else {
12
+ t.fail++;
13
+ std.printf(' FAIL: %s\n', name);
14
+ }
15
+ }
16
+ t.section('btoa/atob');
17
+ const orig = "Hello\u0000World";
18
+ const b64 = btoa(orig);
19
+ const back = atob(b64);
20
+ assert('btoa/atob roundtrip', back === orig);
21
+ assert('btoa produces base64 chars', /^[A-Za-z0-9+/=]+$/.test(b64));
22
+ const hash = new Uint8Array([0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12]);
23
+ assert('btoa(SHA-1 sample) matches known value', btoa(String.fromCharCode(...hash)) === "L9ThxnotKPzthJ7hu3bnORuT6xI=");
24
+ t.section('crypto.getRandomValues');
25
+ const buf = new Uint8Array(32);
26
+ crypto.getRandomValues(buf);
27
+ assert('length=32', buf.length === 32);
28
+ assert('not all zeros', buf.some(b => b !== 0));
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('output length=20', hb.length === 20);
34
+ assert('SHA-1(\'Hello\') matches', hb[0] === 0xF7 && hb[1] === 0xFF && hb[19] === 0xF0);
35
+ t.section('TextEncoder');
36
+ const enc = new TextEncoder();
37
+ const encoded = enc.encode("Hello");
38
+ assert('encode length', encoded.length === 5);
39
+ assert('encode bytes', encoded[0] === 0x48 && encoded[4] === 0x6F);
40
+ assert('encoding property', enc.encoding === 'utf-8');
41
+ t.section('SHA-1 + btoa (WebSocket accept key)');
42
+ const MAGIC_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
43
+ const testKey = 'dGhlIHNhbXBsZSBub25jZQ==';
44
+ const concat = testKey + MAGIC_GUID;
45
+ const acceptBuf = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(concat));
46
+ const acceptBytes = new Uint8Array(acceptBuf);
47
+ const acceptB64 = btoa(String.fromCharCode(...acceptBytes));
48
+ assert('RFC 6455 accept key matches', acceptB64 === "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
49
+ let rejected = false;
50
+ try {
51
+ await crypto.subtle.digest("SHA-256", new Uint8Array(1));
52
+ }
53
+ catch {
54
+ rejected = true;
55
+ }
56
+ assert('unsupported algorithm rejected', rejected);
57
+ }
58
+ };
@@ -0,0 +1,60 @@
1
+ import '../lib/polyfill.js'
2
+ import * as std from 'std'
3
+ import { Tester } from './test_helper.js'
4
+
5
+ export const suite = {
6
+ name: 'polyfill',
7
+ run: async (t: Tester) => {
8
+ function assert(name: string, ok: boolean): void {
9
+ if (ok) { t.ok++; std.printf(' PASS: %s\n', name) }
10
+ else { t.fail++; std.printf(' FAIL: %s\n', name) }
11
+ }
12
+
13
+ t.section('btoa/atob')
14
+ const orig = "Hello\u0000World"
15
+ const b64 = btoa(orig)
16
+ const back = atob(b64)
17
+ assert('btoa/atob roundtrip', back === orig)
18
+ assert('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('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('length=32', buf.length === 32)
27
+ assert('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('output length=20', hb.length === 20)
34
+ assert('SHA-1(\'Hello\') matches', hb[0] === 0xF7 && hb[1] === 0xFF && hb[19] === 0xF0)
35
+
36
+ t.section('TextEncoder')
37
+ const enc = new TextEncoder()
38
+ const encoded = enc.encode("Hello")
39
+ assert('encode length', encoded.length === 5)
40
+ assert('encode bytes', encoded[0] === 0x48 && encoded[4] === 0x6F)
41
+ assert('encoding property', enc.encoding === 'utf-8')
42
+
43
+ t.section('SHA-1 + btoa (WebSocket accept key)')
44
+ const MAGIC_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
45
+ const testKey = 'dGhlIHNhbXBsZSBub25jZQ=='
46
+ const concat = testKey + MAGIC_GUID
47
+ const acceptBuf = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(concat))
48
+ const acceptBytes = new Uint8Array(acceptBuf)
49
+ const acceptB64 = btoa(String.fromCharCode(...acceptBytes))
50
+ assert('RFC 6455 accept key matches', acceptB64 === "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=")
51
+
52
+ let rejected = false
53
+ try {
54
+ await crypto.subtle.digest("SHA-256" as any, new Uint8Array(1))
55
+ } catch {
56
+ rejected = true
57
+ }
58
+ assert('unsupported algorithm rejected', rejected)
59
+ }
60
+ }
@@ -0,0 +1,173 @@
1
+ import '../lib/polyfill.js';
2
+ export const suite = {
3
+ name: 'url',
4
+ run: (t) => {
5
+ t.section('http parse');
6
+ const u = new URL('http://example.com');
7
+ t.check('href', 'http://example.com/', u.href);
8
+ t.check('protocol', 'http:', u.protocol);
9
+ t.check('hostname', 'example.com', u.hostname);
10
+ t.check('port', '', u.port);
11
+ t.check('pathname', '/', u.pathname);
12
+ t.check('host', 'example.com', u.host);
13
+ t.check('origin', 'http://example.com', u.origin);
14
+ t.section('full http');
15
+ const u2 = new URL('https://user:pass@host.com:443/path/to?q=1&r=2#frag');
16
+ t.check('protocol', 'https:', u2.protocol);
17
+ t.check('username', 'user', u2.username);
18
+ t.check('password', 'pass', u2.password);
19
+ t.check('hostname', 'host.com', u2.hostname);
20
+ t.check('port', '443', u2.port);
21
+ t.check('pathname', '/path/to', u2.pathname);
22
+ t.check('search', '?q=1&r=2', u2.search);
23
+ t.check('hash', '#frag', u2.hash);
24
+ t.check('host', 'host.com:443', u2.host);
25
+ t.section('query string params');
26
+ t.check('get q', '1', u2.searchParams.get('q'));
27
+ t.check('get r', '2', u2.searchParams.get('r'));
28
+ t.check('has q', true, u2.searchParams.has('q'));
29
+ t.check('has x', false, u2.searchParams.has('x'));
30
+ t.section('relative resolution');
31
+ const b = new URL('./add.wasm', 'http://example.com/base/');
32
+ t.check('relative path', 'http://example.com/base/add.wasm', b.href);
33
+ const b2 = new URL('../other', 'http://example.com/base/');
34
+ t.check('up one dir', 'http://example.com/other', b2.href);
35
+ const b3 = new URL('/abs', 'http://example.com/base/');
36
+ t.check('absolute path', 'http://example.com/abs', b3.href);
37
+ const b4 = new URL('//other.com:8080/path', 'http://example.com/base/');
38
+ t.check('protocol-relative', 'http://other.com:8080/path', b4.href);
39
+ t.section('file URL');
40
+ const f = new URL('file:///C:/Users/test/file.js');
41
+ t.check('protocol', 'file:', f.protocol);
42
+ t.check('hostname', '', f.hostname);
43
+ t.check('pathname', '/C:/Users/test/file.js', f.pathname);
44
+ t.section('file relative resolution');
45
+ const f2 = new URL('./add.wasm', 'file:///C:/Users/test/helper.js');
46
+ t.check('file relative', 'file:///C:/Users/test/add.wasm', f2.href);
47
+ const f3 = new URL('../other.wasm', 'file:///C:/Users/test/sub/helper.js');
48
+ t.check('file up dir', 'file:///C:/Users/test/other.wasm', f3.href);
49
+ t.section('URLSearchParams');
50
+ const p = new URLSearchParams('a=1&b=2&a=3');
51
+ t.check('get a', '1', p.get('a'));
52
+ t.check('getAll a', 2, p.getAll('a').length);
53
+ t.check('get b', '2', p.get('b'));
54
+ t.check('has c', false, p.has('c'));
55
+ t.check('size', 3, p.size);
56
+ t.section('URLSearchParams set/append/delete');
57
+ const p2 = new URLSearchParams();
58
+ p2.append('key', 'val1');
59
+ p2.append('key', 'val2');
60
+ t.check('append 2', 2, p2.getAll('key').length);
61
+ p2.set('key', 'new');
62
+ t.check('set replaces', 1, p2.getAll('key').length);
63
+ t.check('set value', 'new', p2.get('key'));
64
+ p2.append('x', 'y');
65
+ p2.delete('x');
66
+ t.check('delete', false, p2.has('x'));
67
+ t.section('URLSearchParams toString');
68
+ const p3 = new URLSearchParams('q=hello&r=world');
69
+ t.check('toString', 'q=hello&r=world', p3.toString());
70
+ t.section('URLSearchParams iterator');
71
+ const p4 = new URLSearchParams('a=1&b=2');
72
+ const keys = [];
73
+ for (const k of p4.keys())
74
+ keys.push(k);
75
+ t.check('keys', 'a,b', keys.join(','));
76
+ t.section('URL toJSON');
77
+ const j = new URL('http://example.com/path');
78
+ t.check('toJSON', 'http://example.com/path', j.toJSON());
79
+ t.section('URL toString');
80
+ t.check('toString', 'http://example.com/path', j.toString());
81
+ t.section('IPv6');
82
+ const v6 = new URL('http://[::1]:8080/path');
83
+ t.check('hostname', '::1', v6.hostname);
84
+ t.check('port', '8080', v6.port);
85
+ t.check('host', '[::1]:8080', v6.host);
86
+ t.check('pathname', '/path', v6.pathname);
87
+ const v6b = new URL('http://[2001:db8::1]/');
88
+ t.check('IPv6 no port', '2001:db8::1', v6b.hostname);
89
+ t.check('IPv6 host', '[2001:db8::1]', v6b.host);
90
+ const v6c = new URL('http://[::1]:8080');
91
+ t.check('IPv6 origin', 'http://[::1]:8080', v6c.origin);
92
+ t.section('ws/wss');
93
+ const ws = new URL('ws://example.com/socket');
94
+ t.check('ws protocol', 'ws:', ws.protocol);
95
+ t.check('ws hostname', 'example.com', ws.hostname);
96
+ t.check('ws pathname', '/socket', ws.pathname);
97
+ const wss = new URL('wss://secure.com:9090/chat');
98
+ t.check('wss port', '9090', wss.port);
99
+ t.section('relative fragment/query');
100
+ const rf = new URL('#top', 'http://example.com/path');
101
+ t.check('fragment-only', 'http://example.com/path#top', rf.href);
102
+ const rq = new URL('?q=1', 'http://example.com/path');
103
+ t.check('query-only', 'http://example.com/path?q=1', rq.href);
104
+ const rqf = new URL('?a=1#hash', 'http://example.com/path');
105
+ t.check('query+frag', 'http://example.com/path?a=1#hash', rqf.href);
106
+ t.section('path normalization');
107
+ const np1 = new URL('a/../../other', 'http://example.com/base/');
108
+ t.check('multi up', 'http://example.com/other', np1.href);
109
+ const np2 = new URL('/../other', 'http://example.com/base/');
110
+ t.check('root up', 'http://example.com/other', np2.href);
111
+ const np3 = new URL('.', 'http://example.com/base/');
112
+ t.check('dot only', 'http://example.com/base', np3.href);
113
+ const np4 = new URL('', 'http://example.com/base/');
114
+ t.check('empty relative', 'http://example.com/base/', np4.href);
115
+ t.section('IP address');
116
+ const ip = new URL('http://127.0.0.1:8080/path');
117
+ t.check('ip hostname', '127.0.0.1', ip.hostname);
118
+ t.check('ip port', '8080', ip.port);
119
+ t.section('URLSearchParams values/entries/forEach');
120
+ const pv = new URLSearchParams('a=1&b=2');
121
+ const vals = [];
122
+ for (const v of pv.values())
123
+ vals.push(v);
124
+ t.check('values', '1,2', vals.join(','));
125
+ const entries = [];
126
+ for (const [k, v] of pv.entries())
127
+ entries.push(k + '=' + v);
128
+ t.check('entries', 'a=1,b=2', entries.join(','));
129
+ let forEachStr = '';
130
+ pv.forEach((v, k) => { forEachStr += k + '=' + v + '|'; });
131
+ t.check('forEach', 'a=1|b=2|', forEachStr);
132
+ const ps = new URLSearchParams('b=2&a=1&c=3');
133
+ ps.sort();
134
+ t.check('sort', 'a=1&b=2&c=3', ps.toString());
135
+ t.section('URLSearchParams edge cases');
136
+ const pe1 = new URLSearchParams('a=1&b');
137
+ t.check('empty value', '', pe1.get('b'));
138
+ const pe2 = new URLSearchParams('?a=1&b=2');
139
+ t.check('leading ?', '1', pe2.get('a'));
140
+ const pe3 = new URLSearchParams([['x', '10'], ['y', '20']]);
141
+ t.check('array init', '10', pe3.get('x'));
142
+ t.check('array init 2', '20', pe3.get('y'));
143
+ t.check('array size', 2, pe3.size);
144
+ t.section('encoded chars');
145
+ const enc = new URL('http://example.com/hello%20world');
146
+ t.check('encoded pathname', '/hello%20world', enc.pathname);
147
+ const encq = new URL('http://example.com/path?q=a+b&r=%25');
148
+ t.check('encoded query +→space', 'a b', encq.searchParams.get('q'));
149
+ t.check('encoded query %25', '%', encq.searchParams.get('r'));
150
+ t.section('null/undefined base');
151
+ let threw = false;
152
+ try {
153
+ new URL('/path', null);
154
+ }
155
+ catch {
156
+ threw = true;
157
+ }
158
+ t.check('null base throws', true, threw);
159
+ threw = false;
160
+ try {
161
+ new URL('relative');
162
+ }
163
+ catch {
164
+ threw = true;
165
+ }
166
+ t.check('no base throws', true, threw);
167
+ t.section('data URL');
168
+ const d = new URL('data:text/plain,hello');
169
+ t.check('data protocol', 'data:', d.protocol);
170
+ t.check('data hostname', '', d.hostname);
171
+ t.check('data pathname', 'text/plain,hello', d.pathname);
172
+ }
173
+ };
@@ -0,0 +1,183 @@
1
+ import { Tester } from './test_helper.js'
2
+ import '../lib/polyfill.js'
3
+
4
+ export const suite = {
5
+ name: 'url',
6
+ run: (t: Tester) => {
7
+ t.section('http parse')
8
+ const u = new URL('http://example.com')
9
+ t.check('href', 'http://example.com/', u.href)
10
+ t.check('protocol', 'http:', u.protocol)
11
+ t.check('hostname', 'example.com', u.hostname)
12
+ t.check('port', '', u.port)
13
+ t.check('pathname', '/', u.pathname)
14
+ t.check('host', 'example.com', u.host)
15
+ t.check('origin', 'http://example.com', u.origin)
16
+
17
+ t.section('full http')
18
+ const u2 = new URL('https://user:pass@host.com:443/path/to?q=1&r=2#frag')
19
+ t.check('protocol', 'https:', u2.protocol)
20
+ t.check('username', 'user', u2.username)
21
+ t.check('password', 'pass', u2.password)
22
+ t.check('hostname', 'host.com', u2.hostname)
23
+ t.check('port', '443', u2.port)
24
+ t.check('pathname', '/path/to', u2.pathname)
25
+ t.check('search', '?q=1&r=2', u2.search)
26
+ t.check('hash', '#frag', u2.hash)
27
+ t.check('host', 'host.com:443', u2.host)
28
+
29
+ t.section('query string params')
30
+ t.check('get q', '1', u2.searchParams.get('q'))
31
+ t.check('get r', '2', u2.searchParams.get('r'))
32
+ t.check('has q', true, u2.searchParams.has('q'))
33
+ t.check('has x', false, u2.searchParams.has('x'))
34
+
35
+ t.section('relative resolution')
36
+ const b = new URL('./add.wasm', 'http://example.com/base/')
37
+ t.check('relative path', 'http://example.com/base/add.wasm', b.href)
38
+ const b2 = new URL('../other', 'http://example.com/base/')
39
+ t.check('up one dir', 'http://example.com/other', b2.href)
40
+ const b3 = new URL('/abs', 'http://example.com/base/')
41
+ t.check('absolute path', 'http://example.com/abs', b3.href)
42
+ const b4 = new URL('//other.com:8080/path', 'http://example.com/base/')
43
+ t.check('protocol-relative', 'http://other.com:8080/path', b4.href)
44
+
45
+ t.section('file URL')
46
+ const f = new URL('file:///C:/Users/test/file.js')
47
+ t.check('protocol', 'file:', f.protocol)
48
+ t.check('hostname', '', f.hostname)
49
+ t.check('pathname', '/C:/Users/test/file.js', f.pathname)
50
+
51
+ t.section('file relative resolution')
52
+ const f2 = new URL('./add.wasm', 'file:///C:/Users/test/helper.js')
53
+ t.check('file relative', 'file:///C:/Users/test/add.wasm', f2.href)
54
+ const f3 = new URL('../other.wasm', 'file:///C:/Users/test/sub/helper.js')
55
+ t.check('file up dir', 'file:///C:/Users/test/other.wasm', f3.href)
56
+
57
+ t.section('URLSearchParams')
58
+ const p = new URLSearchParams('a=1&b=2&a=3')
59
+ t.check('get a', '1', p.get('a'))
60
+ t.check('getAll a', 2, p.getAll('a').length)
61
+ t.check('get b', '2', p.get('b'))
62
+ t.check('has c', false, p.has('c'))
63
+ t.check('size', 3, p.size)
64
+
65
+ t.section('URLSearchParams set/append/delete')
66
+ const p2 = new URLSearchParams()
67
+ p2.append('key', 'val1')
68
+ p2.append('key', 'val2')
69
+ t.check('append 2', 2, p2.getAll('key').length)
70
+ p2.set('key', 'new')
71
+ t.check('set replaces', 1, p2.getAll('key').length)
72
+ t.check('set value', 'new', p2.get('key'))
73
+ p2.append('x', 'y')
74
+ p2.delete('x')
75
+ t.check('delete', false, p2.has('x'))
76
+
77
+ t.section('URLSearchParams toString')
78
+ const p3 = new URLSearchParams('q=hello&r=world')
79
+ t.check('toString', 'q=hello&r=world', p3.toString())
80
+
81
+ t.section('URLSearchParams iterator')
82
+ const p4 = new URLSearchParams('a=1&b=2')
83
+ const keys: string[] = []
84
+ for (const k of p4.keys()) keys.push(k)
85
+ t.check('keys', 'a,b', keys.join(','))
86
+
87
+ t.section('URL toJSON')
88
+ const j = new URL('http://example.com/path')
89
+ t.check('toJSON', 'http://example.com/path', j.toJSON())
90
+
91
+ t.section('URL toString')
92
+ t.check('toString', 'http://example.com/path', j.toString())
93
+
94
+ t.section('IPv6')
95
+ const v6 = new URL('http://[::1]:8080/path')
96
+ t.check('hostname', '::1', v6.hostname)
97
+ t.check('port', '8080', v6.port)
98
+ t.check('host', '[::1]:8080', v6.host)
99
+ t.check('pathname', '/path', v6.pathname)
100
+ const v6b = new URL('http://[2001:db8::1]/')
101
+ t.check('IPv6 no port', '2001:db8::1', v6b.hostname)
102
+ t.check('IPv6 host', '[2001:db8::1]', v6b.host)
103
+ const v6c = new URL('http://[::1]:8080')
104
+ t.check('IPv6 origin', 'http://[::1]:8080', v6c.origin)
105
+
106
+ t.section('ws/wss')
107
+ const ws = new URL('ws://example.com/socket')
108
+ t.check('ws protocol', 'ws:', ws.protocol)
109
+ t.check('ws hostname', 'example.com', ws.hostname)
110
+ t.check('ws pathname', '/socket', ws.pathname)
111
+ const wss = new URL('wss://secure.com:9090/chat')
112
+ t.check('wss port', '9090', wss.port)
113
+
114
+ t.section('relative fragment/query')
115
+ const rf = new URL('#top', 'http://example.com/path')
116
+ t.check('fragment-only', 'http://example.com/path#top', rf.href)
117
+ const rq = new URL('?q=1', 'http://example.com/path')
118
+ t.check('query-only', 'http://example.com/path?q=1', rq.href)
119
+ const rqf = new URL('?a=1#hash', 'http://example.com/path')
120
+ t.check('query+frag', 'http://example.com/path?a=1#hash', rqf.href)
121
+
122
+ t.section('path normalization')
123
+ const np1 = new URL('a/../../other', 'http://example.com/base/')
124
+ t.check('multi up', 'http://example.com/other', np1.href)
125
+ const np2 = new URL('/../other', 'http://example.com/base/')
126
+ t.check('root up', 'http://example.com/other', np2.href)
127
+ const np3 = new URL('.', 'http://example.com/base/')
128
+ t.check('dot only', 'http://example.com/base', np3.href)
129
+ const np4 = new URL('', 'http://example.com/base/')
130
+ t.check('empty relative', 'http://example.com/base/', np4.href)
131
+
132
+ t.section('IP address')
133
+ const ip = new URL('http://127.0.0.1:8080/path')
134
+ t.check('ip hostname', '127.0.0.1', ip.hostname)
135
+ t.check('ip port', '8080', ip.port)
136
+
137
+ t.section('URLSearchParams values/entries/forEach')
138
+ const pv = new URLSearchParams('a=1&b=2')
139
+ const vals: string[] = []
140
+ for (const v of pv.values()) vals.push(v)
141
+ t.check('values', '1,2', vals.join(','))
142
+ const entries: string[] = []
143
+ for (const [k, v] of pv.entries()) entries.push(k + '=' + v)
144
+ t.check('entries', 'a=1,b=2', entries.join(','))
145
+ let forEachStr = ''
146
+ pv.forEach((v, k) => { forEachStr += k + '=' + v + '|' })
147
+ t.check('forEach', 'a=1|b=2|', forEachStr)
148
+ const ps = new URLSearchParams('b=2&a=1&c=3')
149
+ ps.sort()
150
+ t.check('sort', 'a=1&b=2&c=3', ps.toString())
151
+
152
+ t.section('URLSearchParams edge cases')
153
+ const pe1 = new URLSearchParams('a=1&b')
154
+ t.check('empty value', '', pe1.get('b'))
155
+ const pe2 = new URLSearchParams('?a=1&b=2')
156
+ t.check('leading ?', '1', pe2.get('a'))
157
+ const pe3 = new URLSearchParams([['x', '10'], ['y', '20']] as any)
158
+ t.check('array init', '10', pe3.get('x'))
159
+ t.check('array init 2', '20', pe3.get('y'))
160
+ t.check('array size', 2, pe3.size)
161
+
162
+ t.section('encoded chars')
163
+ const enc = new URL('http://example.com/hello%20world')
164
+ t.check('encoded pathname', '/hello%20world', enc.pathname)
165
+ const encq = new URL('http://example.com/path?q=a+b&r=%25')
166
+ t.check('encoded query +→space', 'a b', encq.searchParams.get('q'))
167
+ t.check('encoded query %25', '%', encq.searchParams.get('r'))
168
+
169
+ t.section('null/undefined base')
170
+ let threw = false
171
+ try { new URL('/path', null as any) } catch { threw = true }
172
+ t.check('null base throws', true, threw)
173
+ threw = false
174
+ try { new URL('relative') } catch { threw = true }
175
+ t.check('no base throws', true, threw)
176
+
177
+ t.section('data URL')
178
+ const d = new URL('data:text/plain,hello')
179
+ t.check('data protocol', 'data:', d.protocol)
180
+ t.check('data hostname', '', d.hostname)
181
+ t.check('data pathname', 'text/plain,hello', d.pathname)
182
+ }
183
+ }
@@ -0,0 +1,82 @@
1
+ import { readWasmFile } from './test_helper.js';
2
+ export const suite = {
3
+ name: 'wasm-basic',
4
+ run: (t) => {
5
+ t.section('validate');
6
+ t.check('empty buffer', false, WebAssembly.validate(new Uint8Array([]).buffer));
7
+ t.check('invalid bytes', false, WebAssembly.validate(new Uint8Array([1, 2, 3]).buffer));
8
+ const wasmBuf = readWasmFile('./add.wasm');
9
+ if (!wasmBuf) {
10
+ t.fail++;
11
+ return;
12
+ }
13
+ t.check('valid add.wasm', true, WebAssembly.validate(wasmBuf));
14
+ t.section('Module');
15
+ var mod = null;
16
+ try {
17
+ mod = new WebAssembly.Module(wasmBuf);
18
+ t.check('Module constructor', true, mod instanceof WebAssembly.Module);
19
+ }
20
+ catch (e) {
21
+ t.fail++;
22
+ }
23
+ t.section('Module.exports');
24
+ if (mod) {
25
+ try {
26
+ const exps = WebAssembly.Module.exports(mod);
27
+ t.check('exports count', 1, exps.length);
28
+ t.check('export name', 'add', exps[0].name);
29
+ t.check('export kind', 'function', exps[0].kind);
30
+ }
31
+ catch (e) {
32
+ t.fail++;
33
+ }
34
+ }
35
+ t.section('Instance (add.wasm)');
36
+ if (mod) {
37
+ try {
38
+ const inst = new WebAssembly.Instance(mod);
39
+ t.check('Instance created', true, inst instanceof WebAssembly.Instance);
40
+ t.check('add(1, 2)', 3, inst.exports.add(1, 2));
41
+ t.check('add(10, 20)', 30, inst.exports.add(10, 20));
42
+ t.check('add(-5, 3)', -2, inst.exports.add(-5, 3));
43
+ }
44
+ catch (e) {
45
+ t.fail++;
46
+ }
47
+ }
48
+ t.section('complex.wasm');
49
+ const complexBuf = readWasmFile('./complex.wasm');
50
+ if (complexBuf) {
51
+ try {
52
+ const cmod = new WebAssembly.Module(complexBuf);
53
+ const cinst = new WebAssembly.Instance(cmod);
54
+ t.check('add(3, 4)', 7, cinst.exports.add(3, 4));
55
+ t.check('sub(10, 3)', 7, cinst.exports.sub(10, 3));
56
+ t.check('mul(6, 7)', 42, cinst.exports.mul(6, 7));
57
+ t.check('factorial(0)', 1, cinst.exports.factorial(0));
58
+ t.check('factorial(5)', 120, cinst.exports.factorial(5));
59
+ t.check('factorial(10)', 3628800, cinst.exports.factorial(10));
60
+ }
61
+ catch (e) {
62
+ t.fail++;
63
+ }
64
+ }
65
+ t.section('import function');
66
+ const importFuncBuf = readWasmFile('./import_func.wasm');
67
+ if (importFuncBuf) {
68
+ try {
69
+ const ifmod = new WebAssembly.Module(importFuncBuf);
70
+ const ifinst = new WebAssembly.Instance(ifmod, {
71
+ env: { imported_add: (a, b) => a + b }
72
+ });
73
+ t.check('add_via_import(3, 4)', 7, ifinst.exports.add_via_import(3, 4));
74
+ t.check('add_via_import(10, 20)', 30, ifinst.exports.add_via_import(10, 20));
75
+ t.check('add_via_import(-5, 3)', -2, ifinst.exports.add_via_import(-5, 3));
76
+ }
77
+ catch (e) {
78
+ t.fail++;
79
+ }
80
+ }
81
+ }
82
+ };
@@ -0,0 +1,70 @@
1
+ import { Tester, readWasmFile } from './test_helper.js'
2
+
3
+ export const suite = {
4
+ name: 'wasm-basic',
5
+ run: (t: Tester) => {
6
+ t.section('validate')
7
+ t.check('empty buffer', false, WebAssembly.validate(new Uint8Array([]).buffer))
8
+ t.check('invalid bytes', false, WebAssembly.validate(new Uint8Array([1, 2, 3]).buffer))
9
+ const wasmBuf = readWasmFile('./add.wasm')
10
+ if (!wasmBuf) { t.fail++; return }
11
+ t.check('valid add.wasm', true, WebAssembly.validate(wasmBuf))
12
+
13
+ t.section('Module')
14
+ var mod: WebAssembly.Module | null = null
15
+ try {
16
+ mod = new WebAssembly.Module(wasmBuf)
17
+ t.check('Module constructor', true, mod instanceof WebAssembly.Module)
18
+ } catch (e) { t.fail++ }
19
+
20
+ t.section('Module.exports')
21
+ if (mod) {
22
+ try {
23
+ const exps = WebAssembly.Module.exports(mod)
24
+ t.check('exports count', 1, exps.length)
25
+ t.check('export name', 'add', exps[0].name)
26
+ t.check('export kind', 'function', exps[0].kind)
27
+ } catch (e) { t.fail++ }
28
+ }
29
+
30
+ t.section('Instance (add.wasm)')
31
+ if (mod) {
32
+ try {
33
+ const inst = new WebAssembly.Instance(mod)
34
+ t.check('Instance created', true, inst instanceof WebAssembly.Instance)
35
+ t.check('add(1, 2)', 3, inst.exports.add(1, 2))
36
+ t.check('add(10, 20)', 30, inst.exports.add(10, 20))
37
+ t.check('add(-5, 3)', -2, inst.exports.add(-5, 3))
38
+ } catch (e) { t.fail++ }
39
+ }
40
+
41
+ t.section('complex.wasm')
42
+ const complexBuf = readWasmFile('./complex.wasm')
43
+ if (complexBuf) {
44
+ try {
45
+ const cmod = new WebAssembly.Module(complexBuf)
46
+ const cinst = new WebAssembly.Instance(cmod)
47
+ t.check('add(3, 4)', 7, cinst.exports.add(3, 4))
48
+ t.check('sub(10, 3)', 7, cinst.exports.sub(10, 3))
49
+ t.check('mul(6, 7)', 42, cinst.exports.mul(6, 7))
50
+ t.check('factorial(0)', 1, cinst.exports.factorial(0))
51
+ t.check('factorial(5)', 120, cinst.exports.factorial(5))
52
+ t.check('factorial(10)', 3628800, cinst.exports.factorial(10))
53
+ } catch (e) { t.fail++ }
54
+ }
55
+
56
+ t.section('import function')
57
+ const importFuncBuf = readWasmFile('./import_func.wasm')
58
+ if (importFuncBuf) {
59
+ try {
60
+ const ifmod = new WebAssembly.Module(importFuncBuf)
61
+ const ifinst = new WebAssembly.Instance(ifmod, {
62
+ env: { imported_add: (a: number, b: number) => a + b }
63
+ })
64
+ t.check('add_via_import(3, 4)', 7, ifinst.exports.add_via_import(3, 4))
65
+ t.check('add_via_import(10, 20)', 30, ifinst.exports.add_via_import(10, 20))
66
+ t.check('add_via_import(-5, 3)', -2, ifinst.exports.add_via_import(-5, 3))
67
+ } catch (e) { t.fail++ }
68
+ }
69
+ }
70
+ }