@thi.ng/wasm-api 0.2.0 → 0.3.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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2022-08-01T22:09:32Z
3
+ - **Last updated**: 2022-08-04T21:21:08Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -9,6 +9,23 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ## [0.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.3.0) (2022-08-04)
13
+
14
+ #### 🚀 Features
15
+
16
+ - add i64/u64 support/accessors ([768c8bd](https://github.com/thi-ng/umbrella/commit/768c8bd))
17
+ - add WasmBridge.instantiate, add/update accessors ([0698bae](https://github.com/thi-ng/umbrella/commit/0698bae))
18
+ - add WasmBridge.instantiate() boilerplate
19
+ - add setters for typed scalars & arrays
20
+ - rename derefXX() => getXX() getters
21
+ - update tests
22
+ - major update WasmBridge, add types ([47aa222](https://github.com/thi-ng/umbrella/commit/47aa222))
23
+ - add WasmExports base interface
24
+ - add generics for WasmBridge & IWasmAPI
25
+ - update WasmBridge.init() arg (full WASM exports, not just mem)
26
+ - add WasmBridge.exports field to store WASM module exports
27
+ - add naming conflict check in WasmBridge.getImports()
28
+
12
29
  ## [0.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.2.0) (2022-08-01)
13
30
 
14
31
  #### 🚀 Features
package/README.md CHANGED
@@ -25,9 +25,10 @@ Modular, extensible API bridge and generic glue code between JS & WebAssembly.
25
25
 
26
26
  This package provides a small, generic and modular
27
27
  [`WasmBridge`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html)
28
- class as interop basis for hybrid JS/WebAssembly applications. At the moment
29
- only a basic core API is provided (i.e. for debug output, string & pointer
30
- handling), but in the future we aim to also supply support modules for DOM
28
+ class as interop basis and a much reduced boilerplate for hybrid JS/WebAssembly
29
+ applications. At the moment only a minimal core API is provided (i.e. for debug
30
+ output, string, pointer, typed array accessors [8/16/32/64 bit (u)ints, 32/64
31
+ bit floats]), but in the future we aim to also supply support modules for DOM
31
32
  manipulation, WebGL, WebGPU, WebAudio etc.
32
33
 
33
34
  In general, all languages with a WebAssembly target are supported, however
@@ -82,25 +83,26 @@ export const bridge = new WasmBridge({ custom: new CustomAPI() });
82
83
  ```
83
84
 
84
85
  In Zig (or any other language of your choice) we can then utilize this custom
85
- API like so (also see example further below in this readme):
86
+ API like so (Please also see example further below in this readme):
86
87
 
87
88
  ```zig
89
+ // Import JS core API
88
90
  const js = @import("wasmapi");
89
91
 
90
92
  /// JS external to fill vec2 w/ random values
91
93
  extern fn custom_randomVec2(addr: usize) void;
92
94
 
93
95
  export fn test_randomVec2() void {
94
- var foo = [2]f32{ 0, 0 };
96
+ var foo = [2]f32{ 0, 0 };
95
97
 
96
98
  // print original
97
- js.printF32Array(foo[0..]);
99
+ js.printF32Array(foo[0..]);
98
100
 
99
101
  // populate foo with random numbers
100
- custom_randomVec2(@ptrToInt(&foo));
102
+ custom_randomVec2(@ptrToInt(&foo));
101
103
 
102
104
  // print result
103
- js.printF32Array(foo[0..]);
105
+ js.printF32Array(foo[0..]);
104
106
  }
105
107
  ```
106
108
 
@@ -180,7 +182,7 @@ node --experimental-repl-await
180
182
  > const wasmApi = await import("@thi.ng/wasm-api");
181
183
  ```
182
184
 
183
- Package sizes (gzipped, pre-treeshake): ESM: 1.21 KB
185
+ Package sizes (gzipped, pre-treeshake): ESM: 1.63 KB
184
186
 
185
187
  ## Dependencies
186
188
 
@@ -195,34 +197,25 @@ Package sizes (gzipped, pre-treeshake): ESM: 1.21 KB
195
197
  [Generated API docs](https://docs.thi.ng/umbrella/wasm-api/)
196
198
 
197
199
  ```ts
198
- import { WasmBridge } from "@thi.ng/wasm-api";
200
+ import { WasmBridge, WasmExports } from "@thi.ng/wasm-api";
199
201
  import { readFileSync } from "fs";
200
202
 
201
203
  // WASM exports from our dummy module (below)
202
- interface App {
203
- memory: WebAssembly.Memory;
204
+ interface App extends WasmExports {
204
205
  start: () => void;
205
206
  }
206
207
 
207
208
  (async () => {
208
209
  // new API bridge with defaults
209
210
  // (i.e. no child API modules and using console logger)
210
- const bridge = new WasmBridge();
211
+ const bridge = new WasmBridge<App>();
211
212
 
212
213
  // instantiate WASM module using imports provided by the bridge
213
- const wasm = await WebAssembly.instantiate(
214
- readFileSync("hello.wasm"),
215
- bridge.getImports()
216
- );
214
+ // this also initializes any bindings & bridge child APIs (if any)
215
+ await bridge.instantiate(readFileSync("hello.wasm"));
217
216
 
218
- // cast WASM exports to our defined interface
219
- const app: App = <any>wasm.instance.exports;
220
-
221
- // init bindings & child APIs (if any)
222
- await bridge.init(app.memory);
223
-
224
- // call a WASM function
225
- app.start();
217
+ // call an exported WASM function
218
+ bridge.exports.start();
226
219
  })();
227
220
  ```
228
221
 
@@ -238,16 +231,17 @@ export fn start() void {
238
231
  }
239
232
  ```
240
233
 
241
- The WASM binary can be built via (for more complex scenarios add the supplied
242
- .zig file(s) to your `build.zig` and/or source folder):
234
+ The WASM binary can be built using the following command (or for more complex
235
+ scenarios add the supplied .zig file(s) to your `build.zig` and/or source
236
+ folder):
243
237
 
244
238
  ```bash
245
239
  # compile WASM binary
246
240
  zig build-lib \
247
- --pkg-begin wasmapi node_modules/@thi.ng/wasm-api/zig/core.zig --pkg-end \
248
- -target wasm32-freestanding \
249
- -O ReleaseSmall -dynamic --strip \
250
- hello.zig
241
+ --pkg-begin wasmapi node_modules/@thi.ng/wasm-api/zig/core.zig --pkg-end \
242
+ -target wasm32-freestanding \
243
+ -O ReleaseSmall -dynamic --strip \
244
+ hello.zig
251
245
 
252
246
  # disassemble WASM
253
247
  wasm-dis -o hello.wast hello.wasm
package/api.d.ts CHANGED
@@ -1,10 +1,15 @@
1
- import type { Fn } from "@thi.ng/api";
1
+ import type { Fn, Fn2 } from "@thi.ng/api";
2
2
  import type { WasmBridge } from "./bridge.js";
3
+ export declare type BigIntArray = bigint[] | BigInt64Array | BigUint64Array;
3
4
  /**
4
5
  * Common interface for WASM/JS child APIs which will be used in combination
5
6
  * with a parent {@link WasmBridge}.
7
+ *
8
+ * @remarks
9
+ * The generic type param is optional and only used if the API is requiring
10
+ * certain exports declared by WASM module.
6
11
  */
7
- export interface IWasmAPI {
12
+ export interface IWasmAPI<T extends WasmExports = WasmExports> {
8
13
  /**
9
14
  * Called by {@link WasmBridge.init} to initialize all child APIs (async)
10
15
  * after the WASM module has been instantiated. If the method returns false
@@ -12,7 +17,7 @@ export interface IWasmAPI {
12
17
  *
13
18
  * @param parent
14
19
  */
15
- init(parent: WasmBridge): Promise<boolean>;
20
+ init(parent: WasmBridge<T>): Promise<boolean>;
16
21
  /**
17
22
  * Returns an object of this child API's declared WASM imports. Be aware
18
23
  * imports from all child APIs will be merged into a single flat namespace,
@@ -20,6 +25,29 @@ export interface IWasmAPI {
20
25
  */
21
26
  getImports(): WebAssembly.ModuleImports;
22
27
  }
28
+ /**
29
+ * Base interface of exports declared by the WASM module. At the very least, the
30
+ * module needs to export its memory.
31
+ *
32
+ * @remarks
33
+ * This interface is supposed to be extended with the concrete exports defined
34
+ * by your WASM module and is used as generic type param for {@link WasmBridge}
35
+ * and any {@link IWasmAPI} bridge modules. These exports can obtained via
36
+ * {@link WasmBridge.exports} where they will be stored during the execution of
37
+ * {@link WasmBridge.init}.
38
+ */
39
+ export interface WasmExports {
40
+ /**
41
+ * The WASM module's linear memory buffer. The `WasmBridge` automatically
42
+ * creates various typed views of that memory.
43
+ */
44
+ memory: WebAssembly.Memory;
45
+ }
46
+ /**
47
+ * Core API of WASM imports defined by the {@link WasmBridge}. The same
48
+ * functions are declared as bindings in `/zig/core.zig`. Also see this file for
49
+ * documentation of each function...
50
+ */
23
51
  export interface CoreAPI {
24
52
  printI8: Fn<number, void>;
25
53
  printU8: Fn<number, void>;
@@ -30,6 +58,9 @@ export interface CoreAPI {
30
58
  printI32: Fn<number, void>;
31
59
  printU32: Fn<number, void>;
32
60
  printU32Hex: Fn<number, void>;
61
+ _printI64: Fn2<number, number, void>;
62
+ _printU64: Fn2<number, number, void>;
63
+ _printU64Hex: Fn2<number, number, void>;
33
64
  printF32: Fn<number, void>;
34
65
  printF64: Fn<number, void>;
35
66
  _printI8Array: (addr: number, len: number) => void;
@@ -38,6 +69,8 @@ export interface CoreAPI {
38
69
  _printU16Array: (addr: number, len: number) => void;
39
70
  _printI32Array: (addr: number, len: number) => void;
40
71
  _printU32Array: (addr: number, len: number) => void;
72
+ _printI64Array: (addr: number, len: number) => void;
73
+ _printU64Array: (addr: number, len: number) => void;
41
74
  _printF32Array: (addr: number, len: number) => void;
42
75
  _printF64Array: (addr: number, len: number) => void;
43
76
  _printStr0: (addr: number) => void;
package/bridge.d.ts CHANGED
@@ -1,7 +1,24 @@
1
+ import type { NumericArray } from "@thi.ng/api";
1
2
  import type { ILogger } from "@thi.ng/logger";
2
- import type { CoreAPI, IWasmAPI } from "./api.js";
3
- export declare class WasmBridge {
4
- modules: Record<string, IWasmAPI>;
3
+ import type { BigIntArray, CoreAPI, IWasmAPI, WasmExports } from "./api.js";
4
+ /**
5
+ * The main interop API bridge between the JS host environment and a WebAssembly
6
+ * module. This class provides a small core API with various typed accessors and
7
+ * utils to exchange data (scalars, arrays, strings etc.) via the WASM module's
8
+ * memory.
9
+ *
10
+ * @remarks
11
+ * All typed memory accessors are assuming the given lookup addresses are
12
+ * properly aligned to the corresponding primitive types (e.g. f32 values are
13
+ * aligned to 4 byte boundaries, f64 to 8 bytes etc.) Unaligned access is
14
+ * explicitly **not supported**! If you need such, please refer to other
15
+ * mechanisms like JS `DataView`...
16
+ *
17
+ * 64bit integers are handled via JS `BigInt` and hence require the host env to
18
+ * support it. No polyfill is provided.
19
+ */
20
+ export declare class WasmBridge<T extends WasmExports = WasmExports> {
21
+ modules: Record<string, IWasmAPI<T>>;
5
22
  logger: ILogger;
6
23
  i8: Int8Array;
7
24
  u8: Uint8Array;
@@ -9,60 +26,90 @@ export declare class WasmBridge {
9
26
  u16: Uint16Array;
10
27
  i32: Int32Array;
11
28
  u32: Uint32Array;
29
+ i64: BigInt64Array;
30
+ u64: BigUint64Array;
12
31
  f32: Float32Array;
13
32
  f64: Float64Array;
14
33
  utf8Decoder: TextDecoder;
15
34
  utf8Encoder: TextEncoder;
16
35
  core: CoreAPI;
17
- constructor(modules?: Record<string, IWasmAPI>, logger?: ILogger);
18
- init(mem: WebAssembly.Memory): Promise<boolean>;
36
+ exports: T;
37
+ constructor(modules?: Record<string, IWasmAPI<T>>, logger?: ILogger);
19
38
  /**
20
- * Returns object of all WASM imports declared in the bridge core API and
21
- * any provided child APIs.
39
+ * Instantiates WASM module from given `src` (and optional provided extra
40
+ * imports), then automatically calls {@link WasmBridge.init} with the
41
+ * modules exports.
42
+ *
43
+ * @remarks
44
+ * If the given `src` is a `Response` or `Promise<Response>`, the module
45
+ * will be instantiated via `WebAssembly.instantiateStreaming()`, otherwise
46
+ * the non-streaming version will be used.
47
+ *
48
+ * @param src
49
+ * @param imports
22
50
  */
23
- getImports(): {
24
- env: {
25
- printI8: import("@thi.ng/api").Fn<number, void>;
26
- printU8: import("@thi.ng/api").Fn<number, void>;
27
- printU8Hex: import("@thi.ng/api").Fn<number, void>;
28
- printI16: import("@thi.ng/api").Fn<number, void>;
29
- printU16: import("@thi.ng/api").Fn<number, void>;
30
- printU16Hex: import("@thi.ng/api").Fn<number, void>;
31
- printI32: import("@thi.ng/api").Fn<number, void>;
32
- printU32: import("@thi.ng/api").Fn<number, void>;
33
- printU32Hex: import("@thi.ng/api").Fn<number, void>;
34
- printF32: import("@thi.ng/api").Fn<number, void>;
35
- printF64: import("@thi.ng/api").Fn<number, void>;
36
- _printI8Array: (addr: number, len: number) => void;
37
- _printU8Array: (addr: number, len: number) => void;
38
- _printI16Array: (addr: number, len: number) => void;
39
- _printU16Array: (addr: number, len: number) => void;
40
- _printI32Array: (addr: number, len: number) => void;
41
- _printU32Array: (addr: number, len: number) => void;
42
- _printF32Array: (addr: number, len: number) => void;
43
- _printF64Array: (addr: number, len: number) => void;
44
- _printStr0: (addr: number) => void;
45
- _printStr: (addr: number, len: number) => void;
46
- };
47
- };
48
- getI8Array(ptr: number, len: number): Int8Array;
49
- getU8Array(ptr: number, len: number): Uint8Array;
50
- getI16Array(ptr: number, len: number): Int16Array;
51
- getU16Array(ptr: number, len: number): Uint16Array;
52
- getI32Array(ptr: number, len: number): Int32Array;
53
- getU32Array(ptr: number, len: number): Uint32Array;
54
- getF32Array(ptr: number, len: number): Float32Array;
55
- getF64Array(ptr: number, len: number): Float64Array;
56
- derefI8(ptr: number): number;
57
- derefU8(ptr: number): number;
58
- derefI16(ptr: number): number;
59
- derefU16(ptr: number): number;
60
- derefI32(ptr: number): number;
61
- derefU32(ptr: number): number;
62
- derefF32(ptr: number): number;
63
- derefF64(ptr: number): number;
51
+ instantiate(src: Response | BufferSource | PromiseLike<Response | BufferSource>, imports?: WebAssembly.Imports): Promise<boolean>;
52
+ /**
53
+ * Receives the WASM module's exports, stores the for future reference and
54
+ * then initializes all declared bridge child API modules. Returns false if
55
+ * any of the module initializations failed.
56
+ *
57
+ * @param exports
58
+ */
59
+ init(exports: T): Promise<boolean>;
60
+ /**
61
+ * Required use for WASM module instantiation to provide JS imports to the
62
+ * module. Returns an object of all WASM imports declared by the bridge core
63
+ * API and any provided bridge API modules.
64
+ *
65
+ * @remarks
66
+ * Since all declared imports will be merged into a single flat namespace,
67
+ * it's recommended to use per-module naming prefixes to avoid clashes. If
68
+ * there're any naming clashes, this function will throw an error.
69
+ */
70
+ getImports(): WebAssembly.Imports;
71
+ getI8(addr: number): number;
72
+ getU8(addr: number): number;
73
+ getI16(addr: number): number;
74
+ getU16(addr: number): number;
75
+ getI32(addr: number): number;
76
+ getU32(addr: number): number;
77
+ getI64(addr: number): bigint;
78
+ getU64(addr: number): bigint;
79
+ getF32(addr: number): number;
80
+ getF64(addr: number): number;
81
+ setI8(addr: number, x: number): this;
82
+ setU8(addr: number, x: number): this;
83
+ setI16(addr: number, x: number): this;
84
+ setU16(addr: number, x: number): this;
85
+ setI32(addr: number, x: number): this;
86
+ setU32(addr: number, x: number): this;
87
+ setI64(addr: number, x: bigint): this;
88
+ setU64(addr: number, x: bigint): this;
89
+ setF32(addr: number, x: number): this;
90
+ setF64(addr: number, x: number): this;
91
+ getI8Array(addr: number, len: number): Int8Array;
92
+ getU8Array(addr: number, len: number): Uint8Array;
93
+ getI16Array(addr: number, len: number): Int16Array;
94
+ getU16Array(addr: number, len: number): Uint16Array;
95
+ getI32Array(addr: number, len: number): Int32Array;
96
+ getU32Array(addr: number, len: number): Uint32Array;
97
+ getI64Array(addr: number, len: number): BigInt64Array;
98
+ getU64Array(addr: number, len: number): BigUint64Array;
99
+ getF32Array(addr: number, len: number): Float32Array;
100
+ getF64Array(addr: number, len: number): Float64Array;
101
+ setI8Array(addr: number, buf: NumericArray): this;
102
+ setU8Array(addr: number, buf: NumericArray): this;
103
+ setI16Array(addr: number, buf: NumericArray): this;
104
+ setU16Array(addr: number, buf: NumericArray): this;
105
+ setI32Array(addr: number, buf: NumericArray): this;
106
+ setU32Array(addr: number, buf: NumericArray): this;
107
+ setI64Array(addr: number, buf: BigIntArray): this;
108
+ setU64Array(addr: number, buf: BigIntArray): this;
109
+ setF32Array(addr: number, buf: NumericArray): this;
110
+ setF64Array(addr: number, buf: NumericArray): this;
64
111
  getString(addr: number, len?: number): string;
65
- getElementById(addr: number, len?: number): HTMLElement | null;
66
112
  setString(str: string, addr: number, maxBytes: number, terminate?: boolean): number;
113
+ getElementById(addr: number, len?: number): HTMLElement;
67
114
  }
68
115
  //# sourceMappingURL=bridge.d.ts.map