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,80 @@
1
+ import * as std from 'std'
2
+
3
+ const GREEN = '\x1b[32m'
4
+ const RED = '\x1b[31m'
5
+ const BOLD = '\x1b[1m'
6
+ const RESET = '\x1b[0m'
7
+
8
+ function formatDuration(ms: number): string {
9
+ if (ms >= 1000) return (ms / 1000).toFixed(2) + 's'
10
+ return ms + 'ms'
11
+ }
12
+
13
+ export function readWasmFile(path: string): ArrayBuffer | null {
14
+ const base = import.meta.url.slice(0, import.meta.url.lastIndexOf('/') + 1)
15
+ const parts = (base + path).split('/')
16
+ const out: string[] = []
17
+ for (const p of parts) {
18
+ if (p === '..') out.pop()
19
+ else if (p !== '.') out.push(p)
20
+ }
21
+ let filePath = out.join('/').slice(7)
22
+ if (filePath.length >= 3 && filePath[1] === ':') filePath = filePath.slice(1)
23
+ const fp = std.open(filePath, 'rb')
24
+ if (!fp) return null
25
+ fp.seek(0, 2)
26
+ const size = fp.tell()
27
+ fp.seek(0, 0)
28
+ const buffer = new ArrayBuffer(size)
29
+ fp.read(buffer, 0, size)
30
+ fp.close()
31
+ return buffer
32
+ }
33
+
34
+ export class Tester {
35
+ ok = 0
36
+ fail = 0
37
+ private startTime = Date.now()
38
+ private sectionStart = this.startTime
39
+ private lastSection = ''
40
+
41
+ section(name: string): void {
42
+ const now = Date.now()
43
+ if (this.lastSection) {
44
+ const elapsed = now - this.sectionStart
45
+ std.printf(' (%s)\n', formatDuration(elapsed))
46
+ }
47
+ this.sectionStart = now
48
+ this.lastSection = name
49
+ std.printf('\n%s=== %s ===%s\n', BOLD, name, RESET)
50
+ }
51
+
52
+ check(name: string, expected: unknown, actual: unknown): void {
53
+ if (typeof expected === 'number' && typeof actual === 'number') {
54
+ if (Math.abs(expected - actual) < 0.0001) {
55
+ this.ok++; std.printf(' %sPASS:%s %s = %s\n', GREEN, RESET, name, String(actual))
56
+ } else {
57
+ this.fail++; std.printf(' %sFAIL:%s %s = %s (expected %s)\n', RED, RESET, name, String(actual), String(expected))
58
+ }
59
+ } else if (expected === actual) {
60
+ this.ok++; std.printf(' %sPASS:%s %s = %s\n', GREEN, RESET, name, String(actual))
61
+ } else {
62
+ this.fail++; std.printf(' %sFAIL:%s %s = %s (expected %s)\n', RED, RESET, name, String(actual), String(expected))
63
+ }
64
+ }
65
+
66
+ checkTrue(name: string, actual: boolean): void {
67
+ this.check(name, true, actual)
68
+ }
69
+
70
+ summary(): void {
71
+ const now = Date.now()
72
+ if (this.lastSection) {
73
+ const elapsed = now - this.sectionStart
74
+ std.printf(' (%s)\n', formatDuration(elapsed))
75
+ }
76
+ const total = now - this.startTime
77
+ const color = this.fail > 0 ? RED : GREEN
78
+ std.printf('\n%s%d/%d passed (%s)%s\n', color, this.ok, this.ok + this.fail, formatDuration(total), RESET)
79
+ }
80
+ }
@@ -0,0 +1,78 @@
1
+ import * as std from 'std';
2
+ import * as os from 'os';
3
+ function getCacheDir() {
4
+ // import.meta.url: file:///C:/.../_build/test/file.js
5
+ const url = import.meta.url;
6
+ // Strip file:// (7 chars), giving /C:/.../
7
+ let path = url.slice(7);
8
+ // Remove leading / before drive letter
9
+ if (path.length >= 3 && path[1] === ':')
10
+ path = path.slice(1);
11
+ // path now: C:/Users/Docker/quickwin/_build/test/file.js
12
+ // Go up to _build/
13
+ const idx = path.lastIndexOf('/');
14
+ if (idx < 0)
15
+ return '_cache';
16
+ const buildDir = path.slice(0, idx); // .../_build/test
17
+ const idx2 = buildDir.lastIndexOf('/');
18
+ if (idx2 < 0)
19
+ return '_cache';
20
+ return buildDir.slice(0, idx2 + 1) + '_cache'; // .../_build/_cache
21
+ }
22
+ export const suite = {
23
+ name: 'http-import',
24
+ run: async (t) => {
25
+ function assert(name, ok) {
26
+ if (ok) {
27
+ t.ok++;
28
+ std.printf(' PASS: %s\n', name);
29
+ }
30
+ else {
31
+ t.fail++;
32
+ std.printf(' FAIL: %s\n', name);
33
+ }
34
+ }
35
+ const url = 'https://esm.sh/left-pad@1.3.0';
36
+ const cacheDir = getCacheDir();
37
+ t.section('list cache before');
38
+ const [before, err] = os.readdir(cacheDir);
39
+ if (err !== 0) {
40
+ std.printf(' cache dir: %s (error %d, will be created on first request)\n', cacheDir, err);
41
+ }
42
+ const beforeCount = err === 0 ? before.length : 0;
43
+ t.section('import module');
44
+ let mod;
45
+ try {
46
+ mod = await import(url);
47
+ }
48
+ catch (e) {
49
+ assert('import succeeds', false);
50
+ std.printf(' ERROR: %s\n', String(e));
51
+ return;
52
+ }
53
+ assert('import succeeds', true);
54
+ assert('default is function', typeof mod.default === 'function');
55
+ assert('left-pad works', mod.default('hello', 8) === ' hello');
56
+ t.section('check cache');
57
+ const [after, err2] = os.readdir(cacheDir);
58
+ assert('cache dir readable', err2 === 0);
59
+ if (err2 !== 0)
60
+ return;
61
+ assert('cache has body files', after.some(function (f) { return f.endsWith('.body'); }));
62
+ assert('cache has meta files', after.some(function (f) { return f.endsWith('.meta'); }));
63
+ for (const f of after) {
64
+ if (f.endsWith('.meta') && (err !== 0 || before.indexOf(f) < 0)) {
65
+ std.printf(' meta: %s\n', f);
66
+ const fp = std.open(cacheDir + '/' + f, 'r');
67
+ if (fp) {
68
+ const metaStr = fp.readAsString();
69
+ fp.close();
70
+ if (metaStr) {
71
+ const meta = JSON.parse(metaStr);
72
+ std.printf(' storedAt: %d, maxAge: %d\n', meta.storedAt, meta.maxAge);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ };
@@ -0,0 +1,74 @@
1
+ import * as std from 'std'
2
+ import * as os from 'os'
3
+ import { Tester } from './test_helper.js'
4
+
5
+ function getCacheDir(): string {
6
+ // import.meta.url: file:///C:/.../_build/test/file.js
7
+ const url = import.meta.url
8
+ // Strip file:// (7 chars), giving /C:/.../
9
+ let path = url.slice(7)
10
+ // Remove leading / before drive letter
11
+ if (path.length >= 3 && path[1] === ':') path = path.slice(1)
12
+ // path now: C:/Users/Docker/quickwin/_build/test/file.js
13
+ // Go up to _build/
14
+ const idx = path.lastIndexOf('/')
15
+ if (idx < 0) return '_cache'
16
+ const buildDir = path.slice(0, idx) // .../_build/test
17
+ const idx2 = buildDir.lastIndexOf('/')
18
+ if (idx2 < 0) return '_cache'
19
+ return buildDir.slice(0, idx2 + 1) + '_cache' // .../_build/_cache
20
+ }
21
+
22
+ export const suite = {
23
+ name: 'http-import',
24
+ run: async (t: Tester) => {
25
+ function assert(name: string, ok: boolean): void {
26
+ if (ok) { t.ok++; std.printf(' PASS: %s\n', name) }
27
+ else { t.fail++; std.printf(' FAIL: %s\n', name) }
28
+ }
29
+
30
+ const url = 'https://esm.sh/left-pad@1.3.0'
31
+ const cacheDir = getCacheDir()
32
+
33
+ t.section('list cache before')
34
+ const [before, err] = os.readdir(cacheDir)
35
+ if (err !== 0) {
36
+ std.printf(' cache dir: %s (error %d, will be created on first request)\n', cacheDir, err)
37
+ }
38
+ const beforeCount = err === 0 ? before.length : 0
39
+
40
+ t.section('import module')
41
+ let mod: any
42
+ try {
43
+ mod = await import(url)
44
+ } catch (e) {
45
+ assert('import succeeds', false)
46
+ std.printf(' ERROR: %s\n', String(e))
47
+ return
48
+ }
49
+ assert('import succeeds', true)
50
+ assert('default is function', typeof mod.default === 'function')
51
+ assert('left-pad works', mod.default('hello', 8) === ' hello')
52
+
53
+ t.section('check cache')
54
+ const [after, err2] = os.readdir(cacheDir)
55
+ assert('cache dir readable', err2 === 0)
56
+ if (err2 !== 0) return
57
+ assert('cache has body files', after.some(function(f: string) { return f.endsWith('.body') }))
58
+ assert('cache has meta files', after.some(function(f: string) { return f.endsWith('.meta') }))
59
+ for (const f of after) {
60
+ if (f.endsWith('.meta') && (err !== 0 || before.indexOf(f) < 0)) {
61
+ std.printf(' meta: %s\n', f)
62
+ const fp = std.open(cacheDir + '/' + f, 'r')
63
+ if (fp) {
64
+ const metaStr = fp.readAsString()
65
+ fp.close()
66
+ if (metaStr) {
67
+ const meta = JSON.parse(metaStr)
68
+ std.printf(' storedAt: %d, maxAge: %d\n', meta.storedAt, meta.maxAge)
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,69 @@
1
+ import * as std from 'std';
2
+ import '../lib/polyfill.js';
3
+ function loadWasmBytes(path) {
4
+ const fp = std.open(path, 'rb');
5
+ if (!fp)
6
+ throw new Error('Cannot open: ' + path);
7
+ fp.seek(0, 2);
8
+ const size = fp.tell();
9
+ fp.seek(0, 0);
10
+ const buffer = new ArrayBuffer(size);
11
+ fp.read(buffer, 0, size);
12
+ fp.close();
13
+ return buffer;
14
+ }
15
+ function setupMupdfModule(wasmPath) {
16
+ const wasmBinary = loadWasmBytes(wasmPath);
17
+ globalThis["$libmupdf_wasm_Module"] = {
18
+ wasmBinary,
19
+ locateFile: (p) => p
20
+ };
21
+ }
22
+ export const suite = {
23
+ name: 'mupdf-render',
24
+ run: async (t) => {
25
+ try {
26
+ setupMupdfModule('./vendor/mupdf-wasm/mupdf-wasm.wasm');
27
+ const mupdf = await import('../vendor/mupdf-wasm/mupdf.js');
28
+ const fp = std.open('./example.pdf', 'rb');
29
+ if (!fp) {
30
+ console.log('SKIP: no example.pdf');
31
+ return;
32
+ }
33
+ fp.seek(0, 2);
34
+ const size = fp.tell();
35
+ fp.seek(0, 0);
36
+ const buf = new ArrayBuffer(size);
37
+ fp.read(buf, 0, size);
38
+ fp.close();
39
+ console.log('pdf size:', size);
40
+ const doc = mupdf.Document.openDocument(new Uint8Array(buf), "application/pdf");
41
+ console.log('pages:', doc.countPages());
42
+ const page = doc.loadPage(0);
43
+ const bounds = page.getBounds();
44
+ if (bounds && bounds.length === 4)
45
+ console.log('page size:', bounds[2] - bounds[0], 'x', bounds[3] - bounds[1]);
46
+ const pixmap = page.toPixmap(mupdf.Matrix.scale(2, 2), mupdf.ColorSpace.DeviceRGB, false);
47
+ if (pixmap) {
48
+ console.log('pixmap:', pixmap.getWidth(), 'x', pixmap.getHeight());
49
+ const png = pixmap.asPNG();
50
+ console.log('png size:', png.byteLength);
51
+ const out = std.open('./output.png', 'wb');
52
+ if (out) {
53
+ const pngBuf = new ArrayBuffer(png.byteLength);
54
+ new Uint8Array(pngBuf).set(png);
55
+ out.write(pngBuf, 0, png.byteLength);
56
+ out.close();
57
+ }
58
+ console.log('saved to output.png');
59
+ }
60
+ doc.destroy();
61
+ console.log('done');
62
+ t.ok++;
63
+ }
64
+ catch (error) {
65
+ console.log('ERROR:', error);
66
+ t.fail++;
67
+ }
68
+ }
69
+ };
@@ -0,0 +1,74 @@
1
+ import * as std from 'std'
2
+ import { Tester } from './test_helper.js'
3
+ import '../lib/polyfill.js'
4
+
5
+ function loadWasmBytes(path: string): ArrayBuffer {
6
+ const fp = std.open(path, 'rb')
7
+ if (!fp) throw new Error('Cannot open: ' + path)
8
+ fp.seek(0, 2)
9
+ const size = fp.tell()
10
+ fp.seek(0, 0)
11
+ const buffer = new ArrayBuffer(size)
12
+ fp.read(buffer, 0, size)
13
+ fp.close()
14
+ return buffer
15
+ }
16
+
17
+ function setupMupdfModule(wasmPath: string) {
18
+ const wasmBinary = loadWasmBytes(wasmPath)
19
+ ;(globalThis as any)["$libmupdf_wasm_Module"] = {
20
+ wasmBinary,
21
+ locateFile: (p: string) => p
22
+ }
23
+ }
24
+
25
+ export const suite = {
26
+ name: 'mupdf-render',
27
+ run: async (t: Tester): Promise<void> => {
28
+ try {
29
+ setupMupdfModule('./vendor/mupdf-wasm/mupdf-wasm.wasm')
30
+
31
+ const mupdf: typeof import('../vendor/mupdf-wasm/mupdf.js') = await import('../vendor/mupdf-wasm/mupdf.js')
32
+
33
+ const fp = std.open('./example.pdf', 'rb')
34
+ if (!fp) { console.log('SKIP: no example.pdf'); return }
35
+ fp.seek(0, 2)
36
+ const size = fp.tell()
37
+ fp.seek(0, 0)
38
+ const buf = new ArrayBuffer(size)
39
+ fp.read(buf, 0, size)
40
+ fp.close()
41
+ console.log('pdf size:', size)
42
+
43
+ const doc = mupdf.Document.openDocument(new Uint8Array(buf), "application/pdf")
44
+ console.log('pages:', doc.countPages())
45
+
46
+ const page = doc.loadPage(0)
47
+ const bounds = page.getBounds()
48
+ if (bounds && bounds.length === 4)
49
+ console.log('page size:', bounds[2] - bounds[0], 'x', bounds[3] - bounds[1])
50
+
51
+ const pixmap = page.toPixmap(mupdf.Matrix.scale(2, 2), mupdf.ColorSpace.DeviceRGB, false)
52
+ if (pixmap) {
53
+ console.log('pixmap:', pixmap.getWidth(), 'x', pixmap.getHeight())
54
+ const png = pixmap.asPNG()
55
+ console.log('png size:', png.byteLength)
56
+ const out = std.open('./output.png', 'wb')
57
+ if (out) {
58
+ const pngBuf = new ArrayBuffer(png.byteLength)
59
+ new Uint8Array(pngBuf).set(png)
60
+ out.write(pngBuf, 0, png.byteLength)
61
+ out.close()
62
+ }
63
+ console.log('saved to output.png')
64
+ }
65
+
66
+ doc.destroy()
67
+ console.log('done')
68
+ t.ok++
69
+ } catch (error) {
70
+ console.log('ERROR:', error)
71
+ t.fail++
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,77 @@
1
+ import '../lib/polyfill.js';
2
+ import * as std from 'std';
3
+ function loadWasmBytes(path) {
4
+ const fp = std.open(path, 'rb');
5
+ if (!fp)
6
+ throw new Error('Cannot open: ' + path);
7
+ fp.seek(0, 2);
8
+ const size = fp.tell();
9
+ fp.seek(0, 0);
10
+ const buffer = new ArrayBuffer(size);
11
+ fp.read(buffer, 0, size);
12
+ fp.close();
13
+ return buffer;
14
+ }
15
+ export const suite = {
16
+ name: 'mupdf-twice',
17
+ run: async (t) => {
18
+ try {
19
+ console.log(' Loading WASM binary...');
20
+ const wasmBinary = loadWasmBytes('./vendor/mupdf-wasm/mupdf-wasm.wasm');
21
+ console.log(' WASM binary loaded, size:', wasmBinary.byteLength);
22
+ globalThis["$libmupdf_wasm_Module"] = {
23
+ wasmBinary,
24
+ locateFile: (p) => p
25
+ };
26
+ console.log(' Importing mupdf module...');
27
+ const mupdf = await import('../vendor/mupdf-wasm/mupdf.js');
28
+ console.log(' mupdf module imported ok');
29
+ const fp = std.open('./example.pdf', 'rb');
30
+ if (!fp) {
31
+ console.log('SKIP: no example.pdf');
32
+ return;
33
+ }
34
+ fp.seek(0, 2);
35
+ const size = fp.tell();
36
+ fp.seek(0, 0);
37
+ const pdfBuf = new ArrayBuffer(size);
38
+ fp.read(pdfBuf, 0, size);
39
+ fp.close();
40
+ console.log(' PDF loaded, size:', size);
41
+ t.section('Test 1: two toPixmap on same page (no destroy)');
42
+ try {
43
+ const doc = mupdf.Document.openDocument(new Uint8Array(pdfBuf), "application/pdf");
44
+ const page = doc.loadPage(0);
45
+ console.log(' 1: toPixmap #1...');
46
+ const pm1 = page.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false);
47
+ console.log(' 1: toPixmap #1 done:', pm1.getWidth(), 'x', pm1.getHeight());
48
+ t.check('toPixmap #1 ok', true, pm1 !== null);
49
+ console.log(' 1: toPixmap #2...');
50
+ const pm2 = page.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false);
51
+ console.log(' 1: toPixmap #2 done:', pm2.getWidth(), 'x', pm2.getHeight());
52
+ t.check('toPixmap #2 ok', true, pm2 !== null);
53
+ }
54
+ catch (e) {
55
+ console.log(' 1: ERROR:', e instanceof Error ? e.message : String(e));
56
+ }
57
+ t.section('Test 2: second openDocument + toPixmap (no destroy of first)');
58
+ try {
59
+ console.log(' 2: openDocument #2...');
60
+ const doc2 = mupdf.Document.openDocument(new Uint8Array(pdfBuf), "application/pdf");
61
+ console.log(' 2: loadPage #2...');
62
+ const page2 = doc2.loadPage(0);
63
+ console.log(' 2: toPixmap #3...');
64
+ const pm3 = page2.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false);
65
+ console.log(' 2: toPixmap #3 done:', pm3.getWidth(), 'x', pm3.getHeight());
66
+ t.check('toPixmap #3 ok', true, pm3 !== null);
67
+ }
68
+ catch (e) {
69
+ console.log(' 2: ERROR:', e instanceof Error ? e.message : String(e));
70
+ }
71
+ console.log('All mupdf-twice tests done');
72
+ }
73
+ catch (e) {
74
+ console.log('TOP-LEVEL ERROR:', e instanceof Error ? e.message : String(e));
75
+ }
76
+ }
77
+ };
@@ -0,0 +1,81 @@
1
+ import '../lib/polyfill.js'
2
+ import * as std from 'std'
3
+ import { Tester } from './test_helper.js'
4
+
5
+ function loadWasmBytes(path: string): ArrayBuffer {
6
+ const fp = std.open(path, 'rb')
7
+ if (!fp) throw new Error('Cannot open: ' + path)
8
+ fp.seek(0, 2)
9
+ const size = fp.tell()
10
+ fp.seek(0, 0)
11
+ const buffer = new ArrayBuffer(size)
12
+ fp.read(buffer, 0, size)
13
+ fp.close()
14
+ return buffer
15
+ }
16
+
17
+ export const suite = {
18
+ name: 'mupdf-twice',
19
+ run: async (t: Tester): Promise<void> => {
20
+ try {
21
+ console.log(' Loading WASM binary...')
22
+ const wasmBinary = loadWasmBytes('./vendor/mupdf-wasm/mupdf-wasm.wasm')
23
+ console.log(' WASM binary loaded, size:', wasmBinary.byteLength)
24
+
25
+ ;(globalThis as any)["$libmupdf_wasm_Module"] = {
26
+ wasmBinary,
27
+ locateFile: (p: string) => p
28
+ }
29
+
30
+ console.log(' Importing mupdf module...')
31
+ const mupdf: typeof import('../vendor/mupdf-wasm/mupdf.js') = await import('../vendor/mupdf-wasm/mupdf.js')
32
+ console.log(' mupdf module imported ok')
33
+
34
+ const fp = std.open('./example.pdf', 'rb')
35
+ if (!fp) { console.log('SKIP: no example.pdf'); return }
36
+ fp.seek(0, 2)
37
+ const size = fp.tell()
38
+ fp.seek(0, 0)
39
+ const pdfBuf = new ArrayBuffer(size)
40
+ fp.read(pdfBuf, 0, size)
41
+ fp.close()
42
+ console.log(' PDF loaded, size:', size)
43
+
44
+ t.section('Test 1: two toPixmap on same page (no destroy)')
45
+ try {
46
+ const doc = mupdf.Document.openDocument(new Uint8Array(pdfBuf), "application/pdf")
47
+ const page = doc.loadPage(0)
48
+
49
+ console.log(' 1: toPixmap #1...')
50
+ const pm1 = page.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false)
51
+ console.log(' 1: toPixmap #1 done:', pm1.getWidth(), 'x', pm1.getHeight())
52
+ t.check('toPixmap #1 ok', true, pm1 !== null)
53
+
54
+ console.log(' 1: toPixmap #2...')
55
+ const pm2 = page.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false)
56
+ console.log(' 1: toPixmap #2 done:', pm2.getWidth(), 'x', pm2.getHeight())
57
+ t.check('toPixmap #2 ok', true, pm2 !== null)
58
+ } catch (e: any) {
59
+ console.log(' 1: ERROR:', e instanceof Error ? e.message : String(e))
60
+ }
61
+
62
+ t.section('Test 2: second openDocument + toPixmap (no destroy of first)')
63
+ try {
64
+ console.log(' 2: openDocument #2...')
65
+ const doc2 = mupdf.Document.openDocument(new Uint8Array(pdfBuf), "application/pdf")
66
+ console.log(' 2: loadPage #2...')
67
+ const page2 = doc2.loadPage(0)
68
+ console.log(' 2: toPixmap #3...')
69
+ const pm3 = page2.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false)
70
+ console.log(' 2: toPixmap #3 done:', pm3.getWidth(), 'x', pm3.getHeight())
71
+ t.check('toPixmap #3 ok', true, pm3 !== null)
72
+ } catch (e: any) {
73
+ console.log(' 2: ERROR:', e instanceof Error ? e.message : String(e))
74
+ }
75
+
76
+ console.log('All mupdf-twice tests done')
77
+ } catch (e: any) {
78
+ console.log('TOP-LEVEL ERROR:', e instanceof Error ? e.message : String(e))
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,33 @@
1
+ import { readWasmFile } from './test_helper.js';
2
+ export const suite = {
3
+ name: 'vendor/mupdf-wasm',
4
+ run: (t) => {
5
+ t.section('validate binary');
6
+ const buf = readWasmFile('../vendor/mupdf-wasm/mupdf-wasm.wasm');
7
+ if (!buf) {
8
+ t.fail++;
9
+ return;
10
+ }
11
+ t.check('file size > 0', true, buf.byteLength > 0);
12
+ const header = new Uint8Array(buf);
13
+ t.check('magic', true, header[0] === 0x00 && header[1] === 0x61 && header[2] === 0x73 && header[3] === 0x6d);
14
+ t.check('version 1', true, header[4] === 0x01 && header[5] === 0x00 && header[6] === 0x00 && header[7] === 0x00);
15
+ t.check('WebAssembly.validate', true, WebAssembly.validate(buf));
16
+ t.section('Module');
17
+ var mod = null;
18
+ try {
19
+ mod = new WebAssembly.Module(buf);
20
+ t.ok++;
21
+ }
22
+ catch (e) {
23
+ t.fail++;
24
+ }
25
+ t.section('imports/exports');
26
+ if (mod) {
27
+ const exps = WebAssembly.Module.exports(mod);
28
+ t.check('exports > 0', true, exps.length > 0);
29
+ const imps = WebAssembly.Module.imports(mod);
30
+ t.check('imports', imps.length, imps.length);
31
+ }
32
+ }
33
+ };
@@ -0,0 +1,30 @@
1
+ import { Tester, readWasmFile } from './test_helper.js'
2
+
3
+ export const suite = {
4
+ name: 'vendor/mupdf-wasm',
5
+ run: (t: Tester) => {
6
+ t.section('validate binary')
7
+ const buf = readWasmFile('../vendor/mupdf-wasm/mupdf-wasm.wasm')
8
+ if (!buf) { t.fail++; return }
9
+ t.check('file size > 0', true, buf.byteLength > 0)
10
+ const header = new Uint8Array(buf)
11
+ t.check('magic', true, header[0] === 0x00 && header[1] === 0x61 && header[2] === 0x73 && header[3] === 0x6d)
12
+ t.check('version 1', true, header[4] === 0x01 && header[5] === 0x00 && header[6] === 0x00 && header[7] === 0x00)
13
+ t.check('WebAssembly.validate', true, WebAssembly.validate(buf))
14
+
15
+ t.section('Module')
16
+ var mod: WebAssembly.Module | null = null
17
+ try {
18
+ mod = new WebAssembly.Module(buf)
19
+ t.ok++
20
+ } catch (e) { t.fail++ }
21
+
22
+ t.section('imports/exports')
23
+ if (mod) {
24
+ const exps = WebAssembly.Module.exports(mod)
25
+ t.check('exports > 0', true, exps.length > 0)
26
+ const imps = WebAssembly.Module.imports(mod)
27
+ t.check('imports', imps.length, imps.length)
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,63 @@
1
+ import * as sock from "sock";
2
+ import * as os from "os";
3
+ export const suite = {
4
+ name: 'net-event',
5
+ run: async (t) => {
6
+ await new Promise((resolve, reject) => {
7
+ const s = sock.socket();
8
+ if (!s || s === 0) {
9
+ t.fail++;
10
+ reject(new Error('socket() failed'));
11
+ return;
12
+ }
13
+ var connected = false, gotData = false;
14
+ const hostIP = sock.resolve("httpbin.org");
15
+ if (!hostIP) {
16
+ t.fail++;
17
+ reject(new Error('dns failed'));
18
+ return;
19
+ }
20
+ const timeoutId = os.setTimeout(() => {
21
+ if (!connected) {
22
+ sock.closesocket(s);
23
+ reject(new Error('connect timeout'));
24
+ }
25
+ }, 10000);
26
+ sock.set_on_event(s, (event) => {
27
+ if (event.lNetworkEvents & sock.FD_CONNECT) {
28
+ if (event.iErrorCode[0] === 0) {
29
+ connected = true;
30
+ t.ok++;
31
+ os.clearTimeout(timeoutId);
32
+ const req = "GET / HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n";
33
+ const buf = new ArrayBuffer(req.length);
34
+ const v = new Uint8Array(buf);
35
+ for (let i = 0; i < req.length; i++)
36
+ v[i] = req.charCodeAt(i);
37
+ sock.send(s, buf);
38
+ }
39
+ else {
40
+ sock.closesocket(s);
41
+ reject(new Error('connect error: ' + event.iErrorCode[0]));
42
+ }
43
+ }
44
+ if (event.lNetworkEvents & sock.FD_READ && connected && !gotData) {
45
+ const data = sock.recv(s, 4096);
46
+ if (data && data.byteLength > 0) {
47
+ gotData = true;
48
+ t.ok++;
49
+ }
50
+ }
51
+ if (event.lNetworkEvents & sock.FD_CLOSE) {
52
+ os.clearTimeout(timeoutId);
53
+ sock.closesocket(s);
54
+ if (gotData && connected)
55
+ resolve();
56
+ else
57
+ reject(new Error('test incomplete'));
58
+ }
59
+ });
60
+ sock.connect(s, hostIP, 80);
61
+ });
62
+ }
63
+ };