@thi.ng/wasm-api 0.17.3 → 0.18.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 +12 -1
- package/README.md +10 -2
- package/api.d.ts +12 -0
- package/bridge.d.ts +19 -17
- package/bridge.js +29 -26
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2022-10-
|
|
3
|
+
- **Last updated**: 2022-10-31T23:01:45Z
|
|
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,17 @@ 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.18.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.18.0) (2022-10-31)
|
|
13
|
+
|
|
14
|
+
#### 🚀 Features
|
|
15
|
+
|
|
16
|
+
- update WasmBridge & child API module specs ([6773494](https://github.com/thi-ng/umbrella/commit/6773494))
|
|
17
|
+
- update IWasmAPI interface to declare import ID & module dependencies
|
|
18
|
+
- update WasmBridge ctor (array instead of object of sub-modules)
|
|
19
|
+
- update WasmBridge.init() to initialize modules in dependency order
|
|
20
|
+
- replace illegalArgs() with assert()
|
|
21
|
+
- add [@thi.ng/arrays](https://github.com/thi-ng/umbrella/tree/main/packages/arrays) as dependency, update pkg
|
|
22
|
+
|
|
12
23
|
### [0.17.3](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.17.3) (2022-10-31)
|
|
13
24
|
|
|
14
25
|
#### ♻️ Refactoring
|
package/README.md
CHANGED
|
@@ -505,6 +505,13 @@ following example provides a brief overview:
|
|
|
505
505
|
import { IWasmAPI, WasmBridge } from "@thi.ng/wasm-api";
|
|
506
506
|
|
|
507
507
|
export class CustomAPI implements IWasmAPI {
|
|
508
|
+
// Unique API module identifier to group WASM imports,
|
|
509
|
+
// must match ID used by native code (see further below).
|
|
510
|
+
readonly id = "custom";
|
|
511
|
+
// optionally list IDs of other API modules this module depends on
|
|
512
|
+
// these are used to infer the correct initialization order
|
|
513
|
+
readonly dependencies = [];
|
|
514
|
+
|
|
508
515
|
parent!: WasmBridge;
|
|
509
516
|
|
|
510
517
|
async init(parent: WasmBridge) {
|
|
@@ -538,7 +545,7 @@ export class CustomAPI implements IWasmAPI {
|
|
|
538
545
|
Now we can supply this custom API when creating the main WASM bridge:
|
|
539
546
|
|
|
540
547
|
```ts
|
|
541
|
-
export const bridge = new WasmBridge(
|
|
548
|
+
export const bridge = new WasmBridge([new CustomAPI()]);
|
|
542
549
|
```
|
|
543
550
|
|
|
544
551
|
In Zig (or any other language of your choice) we can then utilize this custom
|
|
@@ -681,7 +688,7 @@ node --experimental-repl-await
|
|
|
681
688
|
> const wasmApi = await import("@thi.ng/wasm-api");
|
|
682
689
|
```
|
|
683
690
|
|
|
684
|
-
Package sizes (gzipped, pre-treeshake): ESM: 7.
|
|
691
|
+
Package sizes (gzipped, pre-treeshake): ESM: 7.13 KB
|
|
685
692
|
|
|
686
693
|
**IMPORTANT:** The package includes multiple language code generators which are
|
|
687
694
|
**not** required for normal use of the API bridge. Hence, the actual package
|
|
@@ -691,6 +698,7 @@ size in production will be MUCH smaller than what's stated here!
|
|
|
691
698
|
|
|
692
699
|
- [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/develop/packages/api)
|
|
693
700
|
- [@thi.ng/args](https://github.com/thi-ng/umbrella/tree/develop/packages/args)
|
|
701
|
+
- [@thi.ng/arrays](https://github.com/thi-ng/umbrella/tree/develop/packages/arrays)
|
|
694
702
|
- [@thi.ng/binary](https://github.com/thi-ng/umbrella/tree/develop/packages/binary)
|
|
695
703
|
- [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/develop/packages/checks)
|
|
696
704
|
- [@thi.ng/compare](https://github.com/thi-ng/umbrella/tree/develop/packages/compare)
|
package/api.d.ts
CHANGED
|
@@ -13,6 +13,18 @@ export declare type BigIntArray = bigint[] | BigInt64Array | BigUint64Array;
|
|
|
13
13
|
* certain exports declared by WASM module.
|
|
14
14
|
*/
|
|
15
15
|
export interface IWasmAPI<T extends WasmExports = WasmExports> {
|
|
16
|
+
/**
|
|
17
|
+
* The unique ID for grouping the WASM imports of this module. MUST be the
|
|
18
|
+
* same as used by the native side of the module.
|
|
19
|
+
*/
|
|
20
|
+
readonly id: string;
|
|
21
|
+
/**
|
|
22
|
+
* IDs of other WASM API modules which this module depends on. Used to infer
|
|
23
|
+
* correct initialization order. The core module (w/ unique ID: `wasmapi`)
|
|
24
|
+
* is always considered an implicit dependency, will be initialized first
|
|
25
|
+
* and MUST NOT be stated here.
|
|
26
|
+
*/
|
|
27
|
+
readonly dependencies?: string[];
|
|
16
28
|
/**
|
|
17
29
|
* Called by {@link WasmBridge.init} to initialize all child APIs (async)
|
|
18
30
|
* after the WASM module has been instantiated. If the method returns false
|
package/bridge.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import type { Event, INotify, Listener, NumericArray } from "@thi.ng/api";
|
|
2
|
+
import type { Event, INotify, IObjectOf, Listener, NumericArray } from "@thi.ng/api";
|
|
3
3
|
import type { ILogger } from "@thi.ng/logger";
|
|
4
4
|
import { BigIntArray, CoreAPI, IWasmAPI, IWasmMemoryAccess, MemorySlice, WasmExports } from "./api.js";
|
|
5
5
|
export declare const Panic: {
|
|
@@ -36,11 +36,11 @@ export declare const OutOfMemoryError: {
|
|
|
36
36
|
* mechanisms like JS `DataView`...
|
|
37
37
|
*
|
|
38
38
|
* 64bit integers are handled via JS `BigInt` and hence require the host env to
|
|
39
|
-
* support it. No
|
|
39
|
+
* support it. No polyfills are provided.
|
|
40
40
|
*/
|
|
41
41
|
export declare class WasmBridge<T extends WasmExports = WasmExports> implements IWasmMemoryAccess, INotify {
|
|
42
|
-
modules: Record<string, IWasmAPI<T>>;
|
|
43
42
|
logger: ILogger;
|
|
43
|
+
readonly id = "wasmapi";
|
|
44
44
|
i8: Int8Array;
|
|
45
45
|
u8: Uint8Array;
|
|
46
46
|
i16: Int16Array;
|
|
@@ -56,7 +56,8 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
|
|
|
56
56
|
imports: WebAssembly.Imports;
|
|
57
57
|
exports: T;
|
|
58
58
|
api: CoreAPI;
|
|
59
|
-
|
|
59
|
+
modules: IObjectOf<IWasmAPI<T>>;
|
|
60
|
+
constructor(modules?: IWasmAPI<T>[], logger?: ILogger);
|
|
60
61
|
/**
|
|
61
62
|
* Instantiates WASM module from given `src` (and optional provided extra
|
|
62
63
|
* imports), then automatically calls {@link WasmBridge.init} with the
|
|
@@ -72,9 +73,10 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
|
|
|
72
73
|
*/
|
|
73
74
|
instantiate(src: Response | BufferSource | PromiseLike<Response | BufferSource>, imports?: WebAssembly.Imports): Promise<boolean>;
|
|
74
75
|
/**
|
|
75
|
-
* Receives the WASM module's exports, stores
|
|
76
|
-
* then initializes all declared bridge child API modules
|
|
77
|
-
* any of the module
|
|
76
|
+
* Receives the WASM module's combined exports, stores them for future
|
|
77
|
+
* reference and then initializes all declared bridge child API modules in
|
|
78
|
+
* their stated dependency order. Returns false if any of the module
|
|
79
|
+
* initializations failed.
|
|
78
80
|
*
|
|
79
81
|
* @remarks
|
|
80
82
|
* Emits the {@link EVENT_MEMORY_CHANGED} event just before returning (and
|
|
@@ -84,11 +86,11 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
|
|
|
84
86
|
*/
|
|
85
87
|
init(exports: T): Promise<boolean>;
|
|
86
88
|
/**
|
|
87
|
-
* Called automatically during initialization
|
|
88
|
-
* the various typed WASM memory views
|
|
89
|
-
* and the previous buffer becoming
|
|
90
|
-
* the {@link EVENT_MEMORY_CHANGED}
|
|
91
|
-
* views had to be updated.
|
|
89
|
+
* Called automatically during initialization and from other memory
|
|
90
|
+
* accessors. Initializes and/or updates the various typed WASM memory views
|
|
91
|
+
* (e.g. after growing the WASM memory and the previous buffer becoming
|
|
92
|
+
* detached). Unless `notify` is false, the {@link EVENT_MEMORY_CHANGED}
|
|
93
|
+
* event will be emitted if the memory views had to be updated.
|
|
92
94
|
*
|
|
93
95
|
* @param notify
|
|
94
96
|
*/
|
|
@@ -99,16 +101,16 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
|
|
|
99
101
|
* API and any provided bridge API modules.
|
|
100
102
|
*
|
|
101
103
|
* @remarks
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
104
|
+
* Each API module's imports will be in their own WASM import object/table,
|
|
105
|
+
* named using the same key which is defined by the JS side of the module
|
|
106
|
+
* via {@link IWasmAPI.id}. The bridge's core API is named `wasmapi` and is
|
|
107
|
+
* reserved.
|
|
106
108
|
*
|
|
107
109
|
* @example
|
|
108
110
|
* The following creates a bridge with a fictional `custom` API module:
|
|
109
111
|
*
|
|
110
112
|
* ```ts
|
|
111
|
-
* const bridge = new WasmBridge(
|
|
113
|
+
* const bridge = new WasmBridge([new CustomAPI()]);
|
|
112
114
|
*
|
|
113
115
|
* // get combined imports object
|
|
114
116
|
* bridge.getImports();
|
package/bridge.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { INotifyMixin } from "@thi.ng/api/mixins/inotify";
|
|
3
|
+
import { topoSort } from "@thi.ng/arrays/topo-sort";
|
|
4
|
+
import { assert } from "@thi.ng/errors/assert";
|
|
3
5
|
import { defError } from "@thi.ng/errors/deferror";
|
|
4
|
-
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
|
|
5
6
|
import { U16, U32, U64BIG, U8 } from "@thi.ng/hex";
|
|
6
7
|
import { ConsoleLogger } from "@thi.ng/logger/console";
|
|
7
8
|
import { EVENT_MEMORY_CHANGED, } from "./api.js";
|
|
@@ -21,12 +22,12 @@ export const OutOfMemoryError = defError(() => "Out of memory");
|
|
|
21
22
|
* mechanisms like JS `DataView`...
|
|
22
23
|
*
|
|
23
24
|
* 64bit integers are handled via JS `BigInt` and hence require the host env to
|
|
24
|
-
* support it. No
|
|
25
|
+
* support it. No polyfills are provided.
|
|
25
26
|
*/
|
|
26
27
|
let WasmBridge = class WasmBridge {
|
|
27
|
-
constructor(modules =
|
|
28
|
-
this.modules = modules;
|
|
28
|
+
constructor(modules = [], logger = new ConsoleLogger("wasm")) {
|
|
29
29
|
this.logger = logger;
|
|
30
|
+
this.id = "wasmapi";
|
|
30
31
|
this.utf8Decoder = new TextDecoder();
|
|
31
32
|
this.utf8Encoder = new TextEncoder();
|
|
32
33
|
const logN = (x) => this.logger.debug(x);
|
|
@@ -67,6 +68,11 @@ let WasmBridge = class WasmBridge {
|
|
|
67
68
|
timer: () => performance.now(),
|
|
68
69
|
epoch: () => BigInt(Date.now()),
|
|
69
70
|
};
|
|
71
|
+
this.modules = modules.reduce((acc, x) => {
|
|
72
|
+
assert(acc[x.id] === undefined && x.id !== this.id, `duplicate API module ID: ${x.id}`);
|
|
73
|
+
acc[x.id] = x;
|
|
74
|
+
return acc;
|
|
75
|
+
}, {});
|
|
70
76
|
}
|
|
71
77
|
/**
|
|
72
78
|
* Instantiates WASM module from given `src` (and optional provided extra
|
|
@@ -90,9 +96,10 @@ let WasmBridge = class WasmBridge {
|
|
|
90
96
|
return this.init(wasm.instance.exports);
|
|
91
97
|
}
|
|
92
98
|
/**
|
|
93
|
-
* Receives the WASM module's exports, stores
|
|
94
|
-
* then initializes all declared bridge child API modules
|
|
95
|
-
* any of the module
|
|
99
|
+
* Receives the WASM module's combined exports, stores them for future
|
|
100
|
+
* reference and then initializes all declared bridge child API modules in
|
|
101
|
+
* their stated dependency order. Returns false if any of the module
|
|
102
|
+
* initializations failed.
|
|
96
103
|
*
|
|
97
104
|
* @remarks
|
|
98
105
|
* Emits the {@link EVENT_MEMORY_CHANGED} event just before returning (and
|
|
@@ -103,7 +110,8 @@ let WasmBridge = class WasmBridge {
|
|
|
103
110
|
async init(exports) {
|
|
104
111
|
this.exports = exports;
|
|
105
112
|
this.ensureMemory(false);
|
|
106
|
-
for (let id
|
|
113
|
+
for (let id of topoSort(this.modules, (module) => module.dependencies)) {
|
|
114
|
+
assert(!!this.modules[id], `missing API module: ${id}`);
|
|
107
115
|
this.logger.debug(`initializing API module: ${id}`);
|
|
108
116
|
const status = await this.modules[id].init(this);
|
|
109
117
|
if (!status)
|
|
@@ -113,11 +121,11 @@ let WasmBridge = class WasmBridge {
|
|
|
113
121
|
return true;
|
|
114
122
|
}
|
|
115
123
|
/**
|
|
116
|
-
* Called automatically during initialization
|
|
117
|
-
* the various typed WASM memory views
|
|
118
|
-
* and the previous buffer becoming
|
|
119
|
-
* the {@link EVENT_MEMORY_CHANGED}
|
|
120
|
-
* views had to be updated.
|
|
124
|
+
* Called automatically during initialization and from other memory
|
|
125
|
+
* accessors. Initializes and/or updates the various typed WASM memory views
|
|
126
|
+
* (e.g. after growing the WASM memory and the previous buffer becoming
|
|
127
|
+
* detached). Unless `notify` is false, the {@link EVENT_MEMORY_CHANGED}
|
|
128
|
+
* event will be emitted if the memory views had to be updated.
|
|
121
129
|
*
|
|
122
130
|
* @param notify
|
|
123
131
|
*/
|
|
@@ -147,16 +155,16 @@ let WasmBridge = class WasmBridge {
|
|
|
147
155
|
* API and any provided bridge API modules.
|
|
148
156
|
*
|
|
149
157
|
* @remarks
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
158
|
+
* Each API module's imports will be in their own WASM import object/table,
|
|
159
|
+
* named using the same key which is defined by the JS side of the module
|
|
160
|
+
* via {@link IWasmAPI.id}. The bridge's core API is named `wasmapi` and is
|
|
161
|
+
* reserved.
|
|
154
162
|
*
|
|
155
163
|
* @example
|
|
156
164
|
* The following creates a bridge with a fictional `custom` API module:
|
|
157
165
|
*
|
|
158
166
|
* ```ts
|
|
159
|
-
* const bridge = new WasmBridge(
|
|
167
|
+
* const bridge = new WasmBridge([new CustomAPI()]);
|
|
160
168
|
*
|
|
161
169
|
* // get combined imports object
|
|
162
170
|
* bridge.getImports();
|
|
@@ -177,11 +185,8 @@ let WasmBridge = class WasmBridge {
|
|
|
177
185
|
*/
|
|
178
186
|
getImports() {
|
|
179
187
|
if (!this.imports) {
|
|
180
|
-
this.imports = {
|
|
188
|
+
this.imports = { [this.id]: this.api };
|
|
181
189
|
for (let id in this.modules) {
|
|
182
|
-
if (this.imports[id] !== undefined) {
|
|
183
|
-
illegalArgs(`attempt to redeclare API module ${id}`);
|
|
184
|
-
}
|
|
185
190
|
this.imports[id] = this.modules[id].getImports();
|
|
186
191
|
}
|
|
187
192
|
}
|
|
@@ -391,9 +396,7 @@ let WasmBridge = class WasmBridge {
|
|
|
391
396
|
this.ensureMemory();
|
|
392
397
|
maxBytes = Math.min(maxBytes, this.u8.length - addr);
|
|
393
398
|
const len = this.utf8Encoder.encodeInto(str, this.u8.subarray(addr, addr + maxBytes)).written;
|
|
394
|
-
|
|
395
|
-
illegalArgs(`error writing string to 0x${U32(addr)} (max. ${maxBytes} bytes, got at least ${str.length})`);
|
|
396
|
-
}
|
|
399
|
+
assert(len != null && len < maxBytes + (terminate ? 0 : 1), `error writing string to 0x${U32(addr)} (max. ${maxBytes} bytes, got at least ${str.length})`);
|
|
397
400
|
if (terminate) {
|
|
398
401
|
this.u8[addr + len] = 0;
|
|
399
402
|
}
|
|
@@ -402,7 +405,7 @@ let WasmBridge = class WasmBridge {
|
|
|
402
405
|
getElementById(addr, len = 0) {
|
|
403
406
|
const id = this.getString(addr, len);
|
|
404
407
|
const el = document.getElementById(id);
|
|
405
|
-
el
|
|
408
|
+
assert(!!el, `missing DOM element #${id}`);
|
|
406
409
|
return el;
|
|
407
410
|
}
|
|
408
411
|
/** {@inheritDoc @thi.ng/api#INotify.addListener} */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/wasm-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Generic, modular, extensible API bridge, polyglot glue code and bindings code generators for hybrid JS & WebAssembly projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@thi.ng/api": "^8.4.4",
|
|
40
40
|
"@thi.ng/args": "^2.2.7",
|
|
41
|
+
"@thi.ng/arrays": "^2.4.0",
|
|
41
42
|
"@thi.ng/binary": "^3.3.8",
|
|
42
43
|
"@thi.ng/checks": "^3.3.2",
|
|
43
44
|
"@thi.ng/compare": "^2.1.14",
|
|
@@ -141,5 +142,5 @@
|
|
|
141
142
|
"status": "alpha",
|
|
142
143
|
"year": 2022
|
|
143
144
|
},
|
|
144
|
-
"gitHead": "
|
|
145
|
+
"gitHead": "7eff3051eb395f460421727b8cf5ef79f09faaa9\n"
|
|
145
146
|
}
|