quickwin 2.3.0 → 3.0.0

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 (99) hide show
  1. package/README.en.md +1 -2
  2. package/README.md +1 -2
  3. package/constants.ts +22 -0
  4. package/enum_map.d.ts +4 -0
  5. package/examples/PdfCanvas.js +1 -1
  6. package/examples/PdfCanvas.tsx +2 -2
  7. package/examples/execd/main.js +491 -217
  8. package/examples/pdf_preview2.js +76 -67
  9. package/examples/pdf_preview2.ts +84 -64
  10. package/examples/pdf_viewer.js +4932 -1029
  11. package/examples/pdf_viewer.tsx +11 -22
  12. package/examples/test_gallery.js +308 -143
  13. package/examples/test_gallery.tsx +12 -7
  14. package/examples/test_react_lazy.js +295 -133
  15. package/examples/test_react_lazy.tsx +4 -3
  16. package/examples/test_react_listbox.js +293 -132
  17. package/examples/test_react_listbox.tsx +3 -2
  18. package/examples/test_root_window.js +283 -126
  19. package/examples/test_tab.js +295 -133
  20. package/examples/test_tab.tsx +4 -3
  21. package/examples/tray_demo.js +37 -30
  22. package/examples/tray_demo.tsx +39 -32
  23. package/lib/bitmap.js +40 -0
  24. package/lib/bitmap.ts +39 -0
  25. package/lib/certs.js +31 -3
  26. package/lib/certs.ts +32 -3
  27. package/lib/dpi.js +30 -0
  28. package/lib/dpi.ts +31 -0
  29. package/lib/dumpRects.js +14 -7
  30. package/lib/dumpRects.ts +14 -6
  31. package/lib/ffi/bind.js +5 -4
  32. package/lib/ffi/bind.ts +9 -8
  33. package/lib/http-server.js +50 -25
  34. package/lib/http-server.ts +51 -23
  35. package/lib/react-qw/components/DateTimePicker.js +6 -7
  36. package/lib/react-qw/components/Input.js +5 -1
  37. package/lib/react-qw/components/ListView.js +8 -6
  38. package/lib/react-qw/components/ScrollView.js +28 -22
  39. package/lib/react-qw/index.js +7 -5
  40. package/lib/react-qw/index.ts +5 -5
  41. package/lib/react-qw/props.js +3 -3
  42. package/lib/react-qw/props.ts +3 -3
  43. package/lib/react-qw/reconciler.js +12 -17
  44. package/lib/react-qw/reconciler.ts +12 -16
  45. package/lib/text-measure.js +9 -6
  46. package/lib/text-measure.ts +9 -6
  47. package/lib/windows/comctl32.js +3 -2
  48. package/lib/windows/comctl32.ts +3 -2
  49. package/lib/windows/gdi32.js +28 -6
  50. package/lib/windows/gdi32.ts +30 -6
  51. package/lib/windows/kernel32.js +9 -0
  52. package/lib/windows/kernel32.ts +9 -0
  53. package/lib/windows/shell32.js +11 -2
  54. package/lib/windows/shell32.ts +13 -2
  55. package/lib/windows/structs.js +51 -0
  56. package/lib/windows/structs.ts +55 -0
  57. package/lib/windows/user32.js +6 -5
  58. package/lib/windows/user32.ts +6 -5
  59. package/main.js +13 -5
  60. package/package.json +2 -2
  61. package/quickwin.d.ts +13 -115
  62. package/qwin-nowasm-x86.exe +0 -0
  63. package/qwin-nowasm.exe +0 -0
  64. package/qwin-x86.exe +0 -0
  65. package/qwin.exe +0 -0
  66. package/test/bench_ffi_callraw.js +3 -4
  67. package/test/bench_ffi_callraw.ts +3 -4
  68. package/test/debug/mupdf_debug.js +4 -22
  69. package/test/debug/wasm_export_debug.js +2 -12
  70. package/test/debug/wasm_names.js +3 -13
  71. package/test/example.pdf +0 -0
  72. package/test/mupdf-cdn.js +40 -0
  73. package/test/mupdf-cdn.ts +43 -0
  74. package/test/test_ffi.js +8 -9
  75. package/test/test_ffi.ts +8 -9
  76. package/test/test_ffi_abi.js +6 -6
  77. package/test/test_ffi_abi.ts +8 -8
  78. package/test/test_ffi_windows.js +82 -11
  79. package/test/test_ffi_windows.ts +85 -11
  80. package/test/test_gdi_draw.js +3 -3
  81. package/test/test_gdi_draw.ts +4 -4
  82. package/test/test_mupdf_render.js +4 -22
  83. package/test/test_mupdf_render.ts +4 -23
  84. package/test/test_mupdf_twice.js +4 -19
  85. package/test/test_mupdf_twice.ts +4 -20
  86. package/test/test_mupdf_wasm.js +10 -4
  87. package/test/test_mupdf_wasm.ts +6 -4
  88. package/test/test_react_complex.js +316 -140
  89. package/test/test_react_counter.js +295 -133
  90. package/test/test_react_flex.js +297 -138
  91. package/test/test_react_hidden.js +296 -134
  92. package/test/test_react_listview_link.js +288 -131
  93. package/test/test_react_notify.js +290 -133
  94. package/test/test_react_render.js +295 -133
  95. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +0 -571
  96. package/vendor/mupdf-wasm/mupdf-wasm.js +0 -2749
  97. package/vendor/mupdf-wasm/mupdf-wasm.wasm +0 -0
  98. package/vendor/mupdf-wasm/mupdf.d.ts +0 -939
  99. package/vendor/mupdf-wasm/mupdf.js +0 -3317
package/main.js CHANGED
@@ -1,6 +1,11 @@
1
- import { CallWindowProc, CreateSystemDpiFont, CreateWindow as CreateWindowWithoutScale, DefWindowProc, GetScaleFactor, GetWindowLongPtr, GetWindowText, RegisterClass, SendMessage, SetWindowProc, ShowWindow, } from "gui";
2
- const scaleFactor = GetScaleFactor();
3
- const dpiFont = CreateSystemDpiFont();
1
+ import { CreateWindow as CreateWindowWithoutScale, DefWindowProc, RegisterClass, SendMessage, SetWindowProc, } from "gui";
2
+ // Win32 直通:CallWindowProc / GetWindowLongPtr / GetWindowText 无引擎状态,
3
+ // 已从 gui 模块摘除,走 lib/windows 的 FFI 绑定
4
+ import { CallWindowProc, GetWindowLongPtr, GetWindowText, GetWindowTextLength, ShowWindow } from "./lib/windows/user32.js";
5
+ import { WCHAR } from "./lib/ffi/bind.js";
6
+ import { createSystemDpiFont, getScaleFactor } from "./lib/dpi.js";
7
+ const scaleFactor = getScaleFactor();
8
+ const dpiFont = createSystemDpiFont();
4
9
  if (!dpiFont)
5
10
  throw new Error('Failed to create DPI font');
6
11
  const CreateWindow = (className, title, style, x, y, cx, cy, parent, id) => {
@@ -26,7 +31,10 @@ function wndProc(hwnd, msg, wParam, lParam) {
26
31
  return 0;
27
32
  if (!list)
28
33
  return 0;
29
- const text = GetWindowText(edit);
34
+ const cap = GetWindowTextLength(edit) + 1;
35
+ const buf = WCHAR.alloc(cap);
36
+ GetWindowText(edit, buf, cap);
37
+ const text = WCHAR.decode(buf);
30
38
  if (text.trim())
31
39
  SendMessage(list, 384 /* LbMsg.ADDSTRING */, 0, text);
32
40
  return 0;
@@ -43,4 +51,4 @@ function wndProc(hwnd, msg, wParam, lParam) {
43
51
  RegisterClass("MainWindow", wndProc);
44
52
  var hwnd = CreateWindow("MainWindow", "Todo List", 13565952 /* WindowStyle.OVERLAPPEDWINDOW */, 0, 0, 600, 400, null, null);
45
53
  if (hwnd)
46
- ShowWindow(hwnd);
54
+ ShowWindow(hwnd, 1 /* ShowWindowCmd.SHOWNORMAL */);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "quickwin",
4
- "version": "2.3.0",
4
+ "version": "3.0.0",
5
5
  "description": "QuickJS Win32 runtime — GUI, networking, WASM, FFI, and more",
6
6
  "bin": {
7
7
  "quickwin": "qwin.exe",
@@ -22,7 +22,6 @@
22
22
  "lib/",
23
23
  "test/",
24
24
  "examples/",
25
- "vendor/",
26
25
  "quickwin.d.ts",
27
26
  "constants.ts",
28
27
  "enum_map.d.ts",
@@ -36,6 +35,7 @@
36
35
  "url": "git+https://github.com/quickwinlabs/quickwin.git"
37
36
  },
38
37
  "devDependencies": {
38
+ "@quickwinlabs/mupdf-wasm": "^1.28.0",
39
39
  "@types/node": "^24.13.2",
40
40
  "@types/react": "^19.2.17",
41
41
  "@types/react-reconciler": "^0.33.0",
package/quickwin.d.ts CHANGED
@@ -421,96 +421,23 @@ declare module "wolfssl" {
421
421
 
422
422
  }
423
423
 
424
- declare module "win" {
425
- /** 与 ffi 的 Ptr<"HMODULE"> 同型(ptrBrand string 键)——FFI 侧 <HMODULE>ptr 位(HINSTANCE 语义)直收 */
426
- type HMODULE = number & { readonly ptrBrand: "HMODULE" };
427
-
428
- function LoadLibrary(libName: string): HMODULE | null;
429
- function GetProcAddress(hModule: HMODULE, procName: string): number | null;
430
- function FreeLibrary(hModule: HMODULE): boolean;
431
- /** GetModuleFileName(hModule?) — 传 HMODULE 或不传(当前进程),返回模块完整路径;失败返回 undefined */
432
- function GetModuleFileName(hModule?: HMODULE): string | undefined;
433
- /** GetModuleHandle(moduleName?) — 不传返回当前 exe 的模块句柄;失败返回 null */
434
- function GetModuleHandle(moduleName?: string): HMODULE | null;
435
- }
436
-
437
424
  declare module "gui" {
438
425
  /** 与 ffi 的 Ptr<"N"> 结构同型(ptrBrand string 键,见 lib/ffi/ctype.ts)——各句柄可在
439
426
  * gui 模块与 FFI 绑定两体系间互认直传(原 unique symbol 键只有 HWND 打通) */
440
427
  type HWND = number & { readonly ptrBrand: "HWND" };
441
428
  type HMENU = number & { readonly ptrBrand: "HMENU" };
442
- type HFONT = number & { readonly ptrBrand: "HFONT" };
443
429
  type HICON = number & { readonly ptrBrand: "HICON" };
444
- type WNDPROC = number & { readonly ptrBrand: "WNDPROC" };
445
- // 枚举唯一定义在 constants.ts(真实 .ts,tsc/esbuild 均内联);此处别名仅供签名引用,
446
- // 使用方请直接 import { WindowStyle } from 'quickwin/constants'。
447
- // Gwlp/ImageType 的成员访问(如 Gwlp.WNDPROC)需写 import('./constants').Gwlp.WNDPROC
448
- // ——类型别名不建命名空间,无法穿透取成员。
449
- type GetWindowCmd = import('./constants').GetWindowCmd;
450
- type ShowWindowCmd = import('./constants').ShowWindowCmd;
451
- type SetWindowPosHwnd = import('./constants').SetWindowPosHwnd;
452
- type Gwlp = import('./constants').Gwlp;
453
- type NotifyIconCmd = import('./constants').NotifyIconCmd;
454
- type ImageType = import('./constants').ImageType;
455
- type IDI = import('./constants').IDI;
456
- type ScrollBar = import('./constants').ScrollBar;
430
+ // 枚举唯一定义在 constants.ts(真实 .ts,tsc/esbuild 均内联);本模块不再转出枚举
431
+ // 别名与 RECT 等结构类型——Win32 直通请走 lib/windows(含常量与结构定义)
457
432
 
458
433
  function RegisterClass(className: string, wndProc?: (hwnd: HWND, msg: number, wParam: number, lParam: number) => number): number;
459
434
  function CreateWindow(className: string, title: string, style: number, x: number, y: number, width: number, height: number, parent: HWND | null, menu: HMENU | null): HWND | null;
460
435
  // 销毁窗口及其所有子窗口,自动清理 WNDPROC 和 JS 引用
461
436
  function DestroyWindow(hwnd: HWND): boolean;
462
- function GetWindow(hwnd: HWND, cmd: GetWindowCmd): HWND;
463
-
464
- function ShowWindow(hwnd: HWND, nCmdShow?: ShowWindowCmd): void;
465
437
  function SetWindowProc(hwnd: HWND, wndProc: (hwnd: HWND, msg: number, wParam: number, lParam: number) => number): void;
466
438
  function DefWindowProc(hwnd: HWND, msg: number, wParam: number, lParam: number): number;
467
- function PostQuitMessage(exitCode: number): void;
468
439
  function SendMessage(hwnd: HWND, msg: number, wParam: number, lParam: number | string): number;
469
- function MessageBox(message: string): void;
470
- function SetWindowText(hwnd: HWND, text: string): void;
471
- function GetWindowText(hwnd: HWND): string;
472
- function GetScaleFactor(): number;
473
- function CreateSystemDpiFont(): HFONT | null;
474
- type GwlpReturnType<T extends Gwlp> = T extends import('./constants').Gwlp.WNDPROC ? WNDPROC : number;
475
- function GetWindowLongPtr<T extends Gwlp>(hwnd: HWND, nIndex: T): GwlpReturnType<T>;
476
- function SetWindowLongPtr(hwnd: HWND, nIndex: Gwlp, newLong: number): number;
477
440
  function UnsetWindowProc(hwnd: HWND): boolean;
478
- function CallWindowProc(wndProc: WNDPROC, hwnd: HWND, msg: number, wParam: number, lParam: number): number;
479
- function SetParent(hwnd: HWND, parent: HWND | null): void;
480
- function EnableWindow(hwnd: HWND, enable: boolean): void;
481
- function SetWindowPos(hwnd: HWND, insertAfter: SetWindowPosHwnd, x: number, y: number, width: number, height: number, flags: number): void;
482
-
483
-
484
-
485
- interface NotifyIconData {
486
- hwnd: HWND
487
- uID?: number
488
- flags?: number
489
- callbackMessage?: number
490
- hIcon?: HICON
491
- tip?: string
492
- }
493
-
494
- function ShellNotifyIcon(cmd: NotifyIconCmd, nid: NotifyIconData): boolean;
495
- /**
496
- * Thin wrapper around LoadImageW. Parameter order matches Win32 LoadImageW: (hinst, name, uType?, cx?, cy?, fuLoad?).
497
- * hinst: null for system resources/file path, number for module handle;
498
- * name: when hinst=null, a system icon IDI or .ico file path; when hinst=module handle, a resource ID (number) or resource name (string);
499
- * uType: ImageType constant, default IMAGE_ICON (returns HICON), pass BITMAP/CURSOR for number return;
500
- * cx/cy: target size (0 for default, use LoadImageFlag.DEFAULTSIZE for system default size);
501
- * fuLoad: LoadImageFlag constants. Note: loading system icons (IDI_*, hinst=null) requires LoadImageFlag.SHARED.
502
- * Example: import { IDI, ImageType, LoadImageFlag } from 'quickwin/constants'
503
- * gui.LoadImage(null, "app.ico", ImageType.ICON, 32, 32, LoadImageFlag.LOADFROMFILE)
504
- * gui.LoadImage(null, IDI.APPLICATION, ImageType.ICON, 0, 0, LoadImageFlag.SHARED)
505
- */
506
- function LoadImage<H extends (import("win").HMODULE) | null, T extends ImageType = import('./constants').ImageType.ICON>(
507
- hinst: H,
508
- name: H extends null ? string | IDI : string | number,
509
- uType?: T,
510
- cx?: number,
511
- cy?: number,
512
- fuLoad?: number
513
- ): T extends import('./constants').ImageType.ICON ? HICON | null : number | null;
514
441
  /**
515
442
  * Loads the first icon group (RT_GROUP_ICON) from this exe, i.e. the custom icon
516
443
  * embedded by rcedit. Works with any resource ID including 0. Returns null if the
@@ -519,46 +446,6 @@ declare module "gui" {
519
446
  * Example: gui.LoadAppIcon() → HICON of the exe's icon.
520
447
  */
521
448
  function LoadAppIcon(cx?: number, cy?: number): HICON | null;
522
- /** 从 BGRA 像素数据创建 32bpp top-down DIB,data 为 length >= width*height*4 的 ArrayBuffer,返回 HBITMAP */
523
- function CreateBitmapFromPixels(width: number, height: number, data: ArrayBuffer): number | null;
524
- function DeleteObject(hObject: number): boolean;
525
- function ImageListCreate(cx: number, cy: number, flags?: number, initial?: number, grow?: number): number | null;
526
- function ImageListAdd(himl: number, hBitmap: number): number;
527
- function ImageListDestroy(himl: number): boolean;
528
-
529
-
530
- function CreatePopupMenu(): HMENU | null;
531
- function AppendMenu(menu: HMENU, flags: number, id: number, text: string): boolean;
532
- function TrackPopupMenu(menu: HMENU, x: number, y: number, flags?: number, hwnd?: HWND): number;
533
- function DestroyMenu(menu: HMENU): boolean;
534
- function SetForegroundWindow(hwnd: HWND): boolean;
535
- /** Returns [x, y] or null */
536
- function GetCursorPos(): [number, number] | null;
537
- /** Returns [width, height] of the primary monitor */
538
- function GetScreenSize(): [number, number];
539
- /** Client-area/mouse rect: { left, top, right, bottom } */
540
- type RECT = { left: number; top: number; right: number; bottom: number };
541
- /** Returns { left, top, right, bottom } of client area, or null if invalid */
542
- function GetClientRect(hwnd: HWND): RECT | null;
543
-
544
- function GetWindowRect(hwnd: HWND): RECT | null;
545
- /** Invalidates client area (rect can be null/undefined for full window) */
546
- function InvalidateRect(hwnd: HWND, rect?: RECT | null, erase?: boolean): void;
547
- /** Forces immediate repaint of invalidated area */
548
- function UpdateWindow(hwnd: HWND): void;
549
- /** Checks if the window handle is valid */
550
- function IsWindow(hwnd: HWND): boolean;
551
- /** Sets scroll info; returns current scroll box position. Signature matches Win32 SetScrollInfo. */
552
- function SetScrollInfo(hwnd: HWND, bar: ScrollBar, info: { pos?: number; page?: number; min?: number; max?: number }, redraw?: boolean): number;
553
-
554
- /** Gets scroll info. Signature matches Win32 GetScrollInfo. */
555
- function GetScrollInfo(hwnd: HWND, bar: ScrollBar): { pos: number; page: number; min: number; max: number; trackPos: number };
556
-
557
- /** Shows or hides a scroll bar. Signature matches Win32 ShowScrollBar. */
558
- function ShowScrollBar(hwnd: HWND, bar: ScrollBar, show: boolean): boolean;
559
-
560
-
561
-
562
449
 
563
450
 
564
451
 
@@ -585,6 +472,12 @@ declare module "gui" {
585
472
  }
586
473
 
587
474
  declare module "ffi" {
475
+ /** 指针品牌唯一定义在 lib/ffi/ctype.ts(此处只转发,不手抄结构)——本模块返回的
476
+ * 句柄 / 地址即对应 C 侧指针位,空值形态同 FFI 层:NULL(0),不是 JS null。 */
477
+ type Ptr<N extends string> = import('./lib/ffi/ctype').Ptr<N>;
478
+ type MaybePtr<N extends string> = import('./lib/ffi/ctype').MaybePtr<N>;
479
+ type HMODULE = Ptr<"HMODULE">;
480
+
588
481
  function ffiCall(func: number, argFrame: ArrayBuffer, retBuf: ArrayBuffer, retIsFp: 0 | 1): void;
589
482
  function bufferPtr(buf: ArrayBuffer): number;
590
483
  function readByte(ptr: number): number;
@@ -592,6 +485,11 @@ declare module "ffi" {
592
485
  function writeByte(ptr: number, value: number): void;
593
486
  function closureNew(argBytes: number, retKind: number, wrapper: (frame: ArrayBuffer, retBuf: ArrayBuffer) => void): number;
594
487
  function closureFree(ptr: number): void;
488
+ /** LoadLibraryW 直映:FFI 自举入口(ffi 自身没有 dlopen,取符号必先装载),
489
+ * 成功为模块句柄 <HMODULE>ptr(HINSTANCE 语义),失败为空指针(0);按真值收窄。 */
490
+ function LoadLibrary(libName: string): MaybePtr<"HMODULE">;
491
+ /** GetProcAddress 直映:成功为函数地址 <FARPROC>ptr,失败为空指针(0)。 */
492
+ function GetProcAddress(hModule: HMODULE, procName: string): MaybePtr<"FARPROC">;
595
493
  }
596
494
 
597
495
 
Binary file
package/qwin-nowasm.exe CHANGED
Binary file
package/qwin-x86.exe CHANGED
Binary file
package/qwin.exe CHANGED
Binary file
@@ -13,7 +13,6 @@
13
13
  // 结束必须 DestroyWindow + std.exit。
14
14
  import * as std from 'std';
15
15
  import * as gui from 'gui';
16
- import * as win from 'win';
17
16
  import * as ffi from 'ffi';
18
17
  import { bind } from '../lib/ffi/bind.js';
19
18
  const WM_NULL = 0x0000;
@@ -37,11 +36,11 @@ function step(msg) {
37
36
  step('stage: imports ok');
38
37
  // ---- proc 地址(B/C 臂绕过 bind 直驱 ffiCall)----
39
38
  function procOf(dll, name) {
40
- const h = win.LoadLibrary(dll);
39
+ const h = ffi.LoadLibrary(dll);
41
40
  if (!h)
42
41
  throw new Error('LoadLibrary(' + dll + ') failed');
43
- const p = win.GetProcAddress(h, name);
44
- if (p === null)
42
+ const p = ffi.GetProcAddress(h, name);
43
+ if (!p)
45
44
  throw new Error('GetProcAddress(' + dll + '!' + name + ') failed');
46
45
  return p;
47
46
  }
@@ -13,7 +13,6 @@
13
13
  // 结束必须 DestroyWindow + std.exit。
14
14
  import * as std from 'std'
15
15
  import * as gui from 'gui'
16
- import * as win from 'win'
17
16
  import * as ffi from 'ffi'
18
17
  import { bind } from '../lib/ffi/bind.js'
19
18
 
@@ -39,10 +38,10 @@ step('stage: imports ok')
39
38
 
40
39
  // ---- proc 地址(B/C 臂绕过 bind 直驱 ffiCall)----
41
40
  function procOf(dll: string, name: string): number {
42
- const h = win.LoadLibrary(dll)
41
+ const h = ffi.LoadLibrary(dll)
43
42
  if (!h) throw new Error('LoadLibrary(' + dll + ') failed')
44
- const p = win.GetProcAddress(h, name)
45
- if (p === null) throw new Error('GetProcAddress(' + dll + '!' + name + ') failed')
43
+ const p = ffi.GetProcAddress(h, name)
44
+ if (!p) throw new Error('GetProcAddress(' + dll + '!' + name + ') failed')
46
45
  return p
47
46
  }
48
47
  const pNop0 = procOf('test/nop.dll', 'nop0')
@@ -1,25 +1,7 @@
1
1
  import * as std from 'std';
2
- function loadWasmBytes(path) {
3
- const fp = std.open(path, 'rb');
4
- if (!fp)
5
- throw new Error('Cannot open: ' + path);
6
- fp.seek(0, 2);
7
- const size = fp.tell();
8
- fp.seek(0, 0);
9
- const buffer = new ArrayBuffer(size);
10
- fp.read(buffer, 0, size);
11
- fp.close();
12
- return buffer;
13
- }
14
- function setupMupdfModule(wasmPath) {
15
- const wasmBinary = loadWasmBytes(wasmPath);
16
- globalThis["$libmupdf_wasm_Module"] = {
17
- wasmBinary,
18
- locateFile: (p) => p
19
- };
20
- }
21
- setupMupdfModule('./vendor/mupdf-wasm/mupdf-wasm.wasm');
22
- await import('../../vendor/mupdf-wasm/mupdf.js');
2
+ import { MUPDF_JS_URL, preloadMupdfWasm } from '../mupdf-cdn.js';
3
+ await preloadMupdfWasm();
4
+ await import(MUPDF_JS_URL);
23
5
  // Debug the instance exports
24
6
  const inst = globalThis["$wasm_inst"];
25
7
  const mod = globalThis["$wasm_mod"];
@@ -39,7 +21,7 @@ console.log('Sf index:', sf_idx, 'Tf index:', tf_idx);
39
21
  console.log('instance exports Sf:', typeof inst.exports['Sf']);
40
22
  console.log('instance exports Tf:', typeof inst.exports['Tf']);
41
23
  // Load a simple WASM to verify bridge works
42
- const fp = std.open('./example.pdf', 'rb');
24
+ const fp = std.open('./test/example.pdf', 'rb');
43
25
  if (!fp) {
44
26
  console.log('cannot open example.pdf');
45
27
  std.exit(1);
@@ -1,15 +1,5 @@
1
- import * as std from 'std';
2
- const fp = std.open('./vendor/mupdf-wasm/mupdf-wasm.wasm', 'rb');
3
- if (!fp) {
4
- console.log('cannot open vendor/mupdf-wasm/mupdf-wasm.wasm');
5
- std.exit(1);
6
- }
7
- fp.seek(0, 2);
8
- const size = fp.tell();
9
- fp.seek(0, 0);
10
- const buf = new ArrayBuffer(size);
11
- fp.read(buf, 0, size);
12
- fp.close();
1
+ import { fetchMupdfWasm } from '../mupdf-cdn.js';
2
+ const buf = await fetchMupdfWasm();
13
3
  const module = new WebAssembly.Module(buf);
14
4
  const exports = WebAssembly.Module.exports(module);
15
5
  const imports = WebAssembly.Module.imports(module);
@@ -1,16 +1,6 @@
1
- import * as std from 'std';
2
- // Load the vendor/mupdf-wasm binary using our pre-load mechanism
3
- const fp = std.open('./vendor/mupdf-wasm/mupdf-wasm.wasm', 'rb');
4
- if (!fp) {
5
- console.log('cannot open vendor/mupdf-wasm/mupdf-wasm.wasm');
6
- std.exit(1);
7
- }
8
- fp.seek(0, 2);
9
- const size = fp.tell();
10
- fp.seek(0, 0);
11
- const buf = new ArrayBuffer(size);
12
- fp.read(buf, 0, size);
13
- fp.close();
1
+ import { fetchMupdfWasm } from '../mupdf-cdn.js';
2
+ // Fetch the mupdf-wasm binary over HTTPS (process-wide HTTP cache applies)
3
+ const buf = await fetchMupdfWasm();
14
4
  // Check exports for Tf
15
5
  const module = new WebAssembly.Module(buf);
16
6
  const exports = WebAssembly.Module.exports(module);
Binary file
@@ -0,0 +1,40 @@
1
+ // ── mupdf-wasm CDN loading ──
2
+ //
3
+ // The runtime loads mupdf from unpkg; the npm package itself only ships
4
+ // types (devDependency @quickwinlabs/mupdf-wasm resolved through the
5
+ // package's exports types mapping — no local vendor copy involved).
6
+ // MUPDF_VERSION must stay in sync with the devDependency in package.json
7
+ // when bumping.
8
+ //
9
+ // Importing this module wires up the HTTPS stack (certs + fetch): the
10
+ // wasm bytes travel through the process-wide HTTP cache next to the
11
+ // executable, so the first fetch is the only network hit.
12
+ import '../lib/certs.js';
13
+ import '../lib/fetch.js';
14
+ export const MUPDF_VERSION = '1.28.0';
15
+ const BASE = `https://unpkg.com/@quickwinlabs/mupdf-wasm@${MUPDF_VERSION}`;
16
+ export const MUPDF_JS_URL = `${BASE}/mupdf.js`;
17
+ export const MUPDF_WASM_URL = `${BASE}/mupdf-wasm.wasm`;
18
+ /** Fetch the wasm binary over HTTPS (HTTP-cached, no filesystem involved). */
19
+ export async function fetchMupdfWasm() {
20
+ const resp = await fetch(MUPDF_WASM_URL);
21
+ if (resp.status !== 200)
22
+ throw new Error(`mupdf wasm fetch failed: HTTP ${resp.status}`);
23
+ const wasmBinary = await resp.arrayBuffer();
24
+ if (wasmBinary.byteLength < 1_000_000)
25
+ throw new Error(`mupdf wasm truncated: ${wasmBinary.byteLength} bytes`);
26
+ return wasmBinary;
27
+ }
28
+ /**
29
+ * Fetch the wasm binary and pre-wire it as $libmupdf_wasm_Module so the
30
+ * mupdf.js module factory instantiates from memory (same injection the
31
+ * local-vendor loading used).
32
+ */
33
+ export async function preloadMupdfWasm() {
34
+ const wasmBinary = await fetchMupdfWasm();
35
+ globalThis.$libmupdf_wasm_Module = {
36
+ wasmBinary,
37
+ locateFile: (p) => p,
38
+ };
39
+ return wasmBinary;
40
+ }
@@ -0,0 +1,43 @@
1
+ // ── mupdf-wasm CDN loading ──
2
+ //
3
+ // The runtime loads mupdf from unpkg; the npm package itself only ships
4
+ // types (devDependency @quickwinlabs/mupdf-wasm resolved through the
5
+ // package's exports types mapping — no local vendor copy involved).
6
+ // MUPDF_VERSION must stay in sync with the devDependency in package.json
7
+ // when bumping.
8
+ //
9
+ // Importing this module wires up the HTTPS stack (certs + fetch): the
10
+ // wasm bytes travel through the process-wide HTTP cache next to the
11
+ // executable, so the first fetch is the only network hit.
12
+
13
+ import '../lib/certs.js'
14
+ import '../lib/fetch.js'
15
+
16
+ export const MUPDF_VERSION = '1.28.0'
17
+ const BASE = `https://unpkg.com/@quickwinlabs/mupdf-wasm@${MUPDF_VERSION}`
18
+
19
+ export const MUPDF_JS_URL = `${BASE}/mupdf.js`
20
+ export const MUPDF_WASM_URL = `${BASE}/mupdf-wasm.wasm`
21
+
22
+ /** Fetch the wasm binary over HTTPS (HTTP-cached, no filesystem involved). */
23
+ export async function fetchMupdfWasm(): Promise<ArrayBuffer> {
24
+ const resp = await fetch(MUPDF_WASM_URL)
25
+ if (resp.status !== 200) throw new Error(`mupdf wasm fetch failed: HTTP ${resp.status}`)
26
+ const wasmBinary = await resp.arrayBuffer()
27
+ if (wasmBinary.byteLength < 1_000_000) throw new Error(`mupdf wasm truncated: ${wasmBinary.byteLength} bytes`)
28
+ return wasmBinary
29
+ }
30
+
31
+ /**
32
+ * Fetch the wasm binary and pre-wire it as $libmupdf_wasm_Module so the
33
+ * mupdf.js module factory instantiates from memory (same injection the
34
+ * local-vendor loading used).
35
+ */
36
+ export async function preloadMupdfWasm(): Promise<ArrayBuffer> {
37
+ const wasmBinary = await fetchMupdfWasm()
38
+ ;(globalThis as any).$libmupdf_wasm_Module = {
39
+ wasmBinary,
40
+ locateFile: (p: string) => p,
41
+ }
42
+ return wasmBinary
43
+ }
package/test/test_ffi.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import * as std from 'std';
2
- import * as win from 'win';
3
2
  import * as ffi from 'ffi';
4
3
  import * as os from 'os';
5
4
  import { bind, bindLib } from '../lib/ffi/bind.js';
@@ -72,12 +71,12 @@ export const suite = {
72
71
  name: 'ffi',
73
72
  run: (t) => {
74
73
  t.section('EnumPrinters');
75
- const hWinspool = win.LoadLibrary('winspool.drv');
76
- t.checkTrue('LoadLibrary("winspool.drv") succeeds', hWinspool !== null);
74
+ const hWinspool = ffi.LoadLibrary('winspool.drv');
75
+ t.checkTrue('LoadLibrary("winspool.drv") succeeds', !!hWinspool);
77
76
  if (!hWinspool)
78
77
  return;
79
- const enumPrinters = win.GetProcAddress(hWinspool, 'EnumPrintersW');
80
- t.checkTrue('GetProcAddress("EnumPrintersW") succeeds', enumPrinters !== null);
78
+ const enumPrinters = ffi.GetProcAddress(hWinspool, 'EnumPrintersW');
79
+ t.checkTrue('GetProcAddress("EnumPrintersW") succeeds', !!enumPrinters);
81
80
  if (!enumPrinters)
82
81
  return;
83
82
  const { EnumPrintersW: EnumPrinters } = bindLib('winspool.drv', {
@@ -141,11 +140,11 @@ export const suite = {
141
140
  std.printf(' no printers installed (skip)\n');
142
141
  }
143
142
  t.section('PTR accepts raw number handles (XP 32-bit fix)');
144
- const hUser32 = win.LoadLibrary('user32.dll');
145
- t.checkTrue('LoadLibrary("user32.dll") succeeds', hUser32 !== null);
143
+ const hUser32 = ffi.LoadLibrary('user32.dll');
144
+ t.checkTrue('LoadLibrary("user32.dll") succeeds', !!hUser32);
146
145
  if (hUser32) {
147
- const getDC = win.GetProcAddress(hUser32, 'GetDC');
148
- t.checkTrue('GetProcAddress("GetDC") succeeds', getDC !== null);
146
+ const getDC = ffi.GetProcAddress(hUser32, 'GetDC');
147
+ t.checkTrue('GetProcAddress("GetDC") succeeds', !!getDC);
149
148
  if (getDC) {
150
149
  const GetDC = bind('user32.dll', 'GetDC', '<>ptr -> <>ptr');
151
150
  const screenDc = GetDC(0);
package/test/test_ffi.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import * as std from 'std'
2
- import * as win from 'win'
3
2
  import * as ffi from 'ffi'
4
3
  import * as os from 'os'
5
4
  import { bind, bindLib } from '../lib/ffi/bind.js'
@@ -75,12 +74,12 @@ export const suite = {
75
74
  name: 'ffi',
76
75
  run: (t: Tester) => {
77
76
  t.section('EnumPrinters')
78
- const hWinspool = win.LoadLibrary('winspool.drv')
79
- t.checkTrue('LoadLibrary("winspool.drv") succeeds', hWinspool !== null)
77
+ const hWinspool = ffi.LoadLibrary('winspool.drv')
78
+ t.checkTrue('LoadLibrary("winspool.drv") succeeds', !!hWinspool)
80
79
  if (!hWinspool) return
81
80
 
82
- const enumPrinters = win.GetProcAddress(hWinspool, 'EnumPrintersW')
83
- t.checkTrue('GetProcAddress("EnumPrintersW") succeeds', enumPrinters !== null)
81
+ const enumPrinters = ffi.GetProcAddress(hWinspool, 'EnumPrintersW')
82
+ t.checkTrue('GetProcAddress("EnumPrintersW") succeeds', !!enumPrinters)
84
83
  if (!enumPrinters) return
85
84
 
86
85
  const { EnumPrintersW: EnumPrinters } = bindLib('winspool.drv', {
@@ -154,11 +153,11 @@ export const suite = {
154
153
  }
155
154
 
156
155
  t.section('PTR accepts raw number handles (XP 32-bit fix)')
157
- const hUser32 = win.LoadLibrary('user32.dll')
158
- t.checkTrue('LoadLibrary("user32.dll") succeeds', hUser32 !== null)
156
+ const hUser32 = ffi.LoadLibrary('user32.dll')
157
+ t.checkTrue('LoadLibrary("user32.dll") succeeds', !!hUser32)
159
158
  if (hUser32) {
160
- const getDC = win.GetProcAddress(hUser32, 'GetDC')
161
- t.checkTrue('GetProcAddress("GetDC") succeeds', getDC !== null)
159
+ const getDC = ffi.GetProcAddress(hUser32, 'GetDC')
160
+ t.checkTrue('GetProcAddress("GetDC") succeeds', !!getDC)
162
161
  if (getDC) {
163
162
  const GetDC = bind('user32.dll', 'GetDC', '<>ptr -> <>ptr')
164
163
  const screenDc = GetDC(0)
@@ -1,5 +1,5 @@
1
1
  import * as std from 'std';
2
- import * as win from 'win';
2
+ import * as ffi from 'ffi';
3
3
  import * as os from 'os';
4
4
  import { bind } from '../lib/ffi/bind.js';
5
5
  import { struct } from '../lib/ffi/struct.js';
@@ -29,15 +29,15 @@ function readAscii(buf, len) {
29
29
  return s;
30
30
  }
31
31
  function has(dll, name) {
32
- return !!dll && !!win.GetProcAddress(dll, name);
32
+ return !!dll && !!ffi.GetProcAddress(dll, name);
33
33
  }
34
34
  export const suite = {
35
35
  name: 'ffi-abi',
36
36
  run: (t) => {
37
- const k32 = win.LoadLibrary('kernel32.dll');
38
- const usr = win.LoadLibrary('user32.dll');
39
- const gdi = win.LoadLibrary('gdi32.dll');
40
- const crt = win.LoadLibrary('msvcrt.dll');
37
+ const k32 = ffi.LoadLibrary('kernel32.dll');
38
+ const usr = ffi.LoadLibrary('user32.dll');
39
+ const gdi = ffi.LoadLibrary('gdi32.dll');
40
+ const crt = ffi.LoadLibrary('msvcrt.dll');
41
41
  t.section('param count 0 ( -> u32)');
42
42
  if (has(k32, 'GetTickCount')) {
43
43
  const getTickCount = bind('kernel32.dll', 'GetTickCount', ' -> u32');
@@ -1,9 +1,9 @@
1
1
  import * as std from 'std'
2
- import * as win from 'win'
2
+ import * as ffi from 'ffi'
3
3
  import * as os from 'os'
4
4
  import { bind } from '../lib/ffi/bind.js'
5
5
  import { struct } from '../lib/ffi/struct.js'
6
- import { NULL, PtrArrayBuffer } from '../lib/ffi/ctype.js'
6
+ import { NULL, PtrArrayBuffer, type MaybePtr } from '../lib/ffi/ctype.js'
7
7
  import { POINT, RECT } from '../lib/windows/structs.js'
8
8
  import { Tester } from './test_helper.js'
9
9
 
@@ -31,17 +31,17 @@ function readAscii(buf: ArrayBuffer, len: number): string {
31
31
  return s
32
32
  }
33
33
 
34
- function has(dll: win.HMODULE | null, name: string): boolean {
35
- return !!dll && !!win.GetProcAddress(dll, name)
34
+ function has(dll: MaybePtr<'HMODULE'>, name: string): boolean {
35
+ return !!dll && !!ffi.GetProcAddress(dll, name)
36
36
  }
37
37
 
38
38
  export const suite = {
39
39
  name: 'ffi-abi',
40
40
  run: (t: Tester) => {
41
- const k32 = win.LoadLibrary('kernel32.dll')
42
- const usr = win.LoadLibrary('user32.dll')
43
- const gdi = win.LoadLibrary('gdi32.dll')
44
- const crt = win.LoadLibrary('msvcrt.dll')
41
+ const k32 = ffi.LoadLibrary('kernel32.dll')
42
+ const usr = ffi.LoadLibrary('user32.dll')
43
+ const gdi = ffi.LoadLibrary('gdi32.dll')
44
+ const crt = ffi.LoadLibrary('msvcrt.dll')
45
45
 
46
46
  t.section('param count 0 ( -> u32)')
47
47
  if (has(k32, 'GetTickCount')) {