Use rollup to bundle the web worker script to support more downstream bundlers

The WASM bundle is embedded as a base64 encoded resource and loaded synchronously, because this is the only mechanism widely supported to load WASM inside a web worker currently. Hopefully in the future this can be changed to pure modules.
This commit is contained in:
Mark Sinclair
2023-01-17 10:38:00 +00:00
parent 548dfed713
commit 69f5a0d6ba
8 changed files with 4142 additions and 114 deletions
@@ -54,6 +54,7 @@
"scripts": {
"prestart": "yarn build:dependencies",
"start": "webpack serve --progress --port 3000",
"start:only-this": "webpack serve --progress --port 3000",
"build:dependencies": "run-s build:dependencies:nym-client-wasm build:dependencies:ts-packages build:dependencies:sdk",
"build:dependencies:nym-client-wasm": "../../packages/nym-client-wasm/scripts/build.sh",
"build:dependencies:ts-packages": "cd ../../../.. && yarn && yarn build",
@@ -1,4 +1,4 @@
import { NymMixnetClient } from '@nymproject/sdk';
import { NymMixnetClient, MimeTypes } from '@nymproject/sdk';
/**
* Create a Sphinx packet and send it to the mixnet through the gateway node.
@@ -8,11 +8,11 @@ import { NymMixnetClient } from '@nymproject/sdk';
* @param {Client} nymClient the nym client to use for message sending
*/
export async function sendMessageTo(nym: NymMixnetClient) {
const payload = (document.getElementById('message') as HTMLFormElement).value;
const message = (document.getElementById('message') as HTMLFormElement).value;
const recipient = (document.getElementById('recipient') as HTMLFormElement).value;
await nym.client.sendMessage({ payload, recipient });
displaySend(payload);
await nym.client.send({ payload: { message, mimeType: MimeTypes.TextPlain }, recipient });
displaySend(message);
}
/**
@@ -14,13 +14,8 @@ async function main() {
return;
}
// // mixnet v1
// const nymApiUrl = 'https://validator.nymtech.net/api';
// const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
// mixnet v2
const nymApiUrl = 'https://qwerty-validator-api.qa.nymte.ch/api'; // "http://localhost:8081";
const preferredGatewayIdentityKey = undefined; // '36vfvEyBzo5cWEFbnP7fqgY39kFw9PQhvwzbispeNaxL';
const nymApiUrl = 'https://validator.nymtech.net/api';
const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
// subscribe to connect event, so that we can show the client's address
nym.events.subscribeToConnected((e) => {
@@ -13,8 +13,8 @@ rm nym_client_wasm* package.json || true
# let wasm-pack build the files and put them in the output location rather than `./pkg`
cd ../../../../clients/webassembly
wasm-pack build --scope nymproject --target no-modules --out-dir ../../sdk/typescript/packages/nym-client-wasm
wasm-pack build --scope nymproject --target web --out-dir ../../sdk/typescript/packages/nym-client-wasm
# clean up some files that come with the build
cd ../../sdk/typescript/packages/nym-client-wasm
rm README.md LICENSE_APACHE
rm README.md LICENSE_APACHE
+18 -7
View File
@@ -27,10 +27,13 @@
"build:dependencies:ts-packages": "cd ../../../.. && yarn && yarn build",
"build:dependencies:nym-client-wasm": "../nym-client-wasm/scripts/build.sh",
"prebuild": "yarn build:dependencies",
"build": "tsc",
"postbuild": "cp ../nym-client-wasm/nym_client_wasm* dist/mixnet/wasm && yarn copy:readme",
"build:only-this": "tsc",
"postbuild:only-this": "cp ../nym-client-wasm/nym_client_wasm* dist/mixnet/wasm",
"build": "yarn build:only-this",
"build:only-this": "run-s build:worker build:tsc build:worker:move",
"postbuild:only-this": "yarn copy:readme",
"build:tsc": "tsc",
"build:worker": "rollup src/mixnet/wasm/worker.ts -c rollup.config.mjs",
"postbuild:worker": "rm -rf dist/index.d.ts dist/coconut dist/mixnet && mkdir -p dist/mixnet/wasm",
"build:worker:move": "rm dist/mixnet/wasm/worker.js && mv dist/worker.js dist/mixnet/wasm",
"copy:readme": "cp README.md dist"
},
"dependencies": {
@@ -38,10 +41,13 @@
},
"devDependencies": {
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-terser": "^0.2.1",
"@rollup/plugin-typescript": "^10.0.1",
"@rollup/plugin-wasm": "^6.1.1",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"rimraf": "^3.0.2",
"typescript": "^4.8.4",
"eslint": "^8.10.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^16.1.0",
@@ -52,6 +58,11 @@
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.2",
"eslint-plugin-react-hooks": "^4.3.0"
"eslint-plugin-react-hooks": "^4.3.0",
"rimraf": "^3.0.2",
"rollup": "^3.9.1",
"rollup-plugin-base64": "^1.0.1",
"rollup-plugin-web-worker-loader": "^1.6.1",
"typescript": "^4.8.4"
}
}
@@ -0,0 +1,28 @@
import typescript from '@rollup/plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';
import { wasm } from '@rollup/plugin-wasm';
import replace from '@rollup/plugin-replace';
const extensions = [
'.js', '.jsx', '.ts', '.tsx',
];
export default {
input: 'src/index.ts',
output: {
dir: 'dist',
format: 'es',
},
plugins: [
resolve({ extensions }),
// this is some nasty monkey patching that removes the WASM URL (because it is handled by the `wasm` plugin)
replace({
values: { 'input = new URL(\'nym_client_wasm_bg.wasm\', import.meta.url);': 'input = undefined;' },
delimiters: ['', ''],
preventAssignment: true,
}),
// force the wasm plugin to embed the wasm bundle - this means no downstream bundlers have to worry about handling it
wasm({ maxFileSize: 10000000, targetEnv: 'browser' }),
typescript(),
],
};
@@ -1,11 +1,30 @@
/* eslint-disable no-console,no-restricted-globals */
/// <reference path="../../../../nym-client-wasm/nym_client_wasm.d.ts" />
/**
* NB: URL syntax is used so that bundlers like webpack can load this package's code when inside the final bundle
* the files from ../../../../nym-client-wasm will be copied into the dist directory of this package, so all import
* paths are _relative to the output directory_ of this package (`dist`) - don't get confused!
*/
import * as Comlink from 'comlink';
//
// Rollup will replace wasmBytes with a function that loads the WASM bundle from a base64 string embedded in the output.
//
// Doing it this way, saves having to support a large variety of bundlers and their quirks.
//
// @ts-ignore
import wasmBytes from '@nymproject/nym-client-wasm/nym_client_wasm_bg.wasm';
import init, {
NymClient,
NymClientBuilder,
Config,
get_gateway,
default_debug,
decode_payload,
parse_utf8_string,
utf8_string_to_byte_array,
encode_payload_with_headers,
} from '@nymproject/nym-client-wasm';
import type {
BinaryMessageReceivedEvent,
ConnectedEvent,
@@ -18,13 +37,11 @@ import type {
import { EventKinds, MimeTypes } from './types';
// web workers are only allowed to load external scripts as the load
importScripts(new URL('./nym_client_wasm.js', import.meta.url));
// importScripts(new URL('./nym_client_wasm.js', import.meta.url));
// eslint-disable-next-line import/extensions
console.log('[Nym WASM client] Starting Nym WASM web worker...');
// again, construct a URL that can be used by a bundler to repackage the WASM binary
const wasmUrl = new URL('./nym_client_wasm_bg.wasm', import.meta.url);
/**
* Helper method to send typed messages.
* @param event The strongly typed message to send back to the calling thread.
@@ -35,7 +52,11 @@ const postMessageWithType = <E>(event: E) => self.postMessage(event);
* This class holds the state of the Nym WASM client and provides any interop needed.
*/
class ClientWrapper {
client: wasm_bindgen.NymClient | null = null;
client: NymClient | null = null;
builder: NymClientBuilder | null = null;
mimeTypes: string[] = [MimeTypes.TextPlain, MimeTypes.ApplicationJson];
/**
* Creates the WASM client and initialises it.
@@ -127,8 +148,8 @@ class ClientWrapper {
}
// load WASM binary
wasm_bindgen(wasmUrl)
.then((importResult) => {
init(wasmBytes())
.then((importResult: any) => {
// sets up better stack traces in case of in-rust panics
importResult.set_panic_hook();
@@ -137,27 +158,14 @@ wasm_bindgen(wasmUrl)
const startHandler = async (config: NymClientConfig) => {
// fetch the gateway details (randomly chosen if no preferred gateway is set)
const gatewayEndpoint = await wasm_bindgen.get_gateway(
config.nymApiUrl,
config.preferredGatewayIdentityKey,
);
const gatewayEndpoint = await get_gateway(config.nymApiUrl, config.preferredGatewayIdentityKey);
// set a different gatewayListener in order to avoid workaround ws over https error
if (config.gatewayListener)
gatewayEndpoint.gateway_listener = config.gatewayListener;
if (config.gatewayListener) gatewayEndpoint.gateway_listener = config.gatewayListener;
// create the client, passing handlers for events
wrapper.init(
new wasm_bindgen.Config(
config.clientId,
config.nymApiUrl,
gatewayEndpoint,
config.debug || wasm_bindgen.default_debug(),
),
() => {
console.log();
},
undefined,
new Config(config.clientId, config.nymApiUrl, gatewayEndpoint, config.debug || default_debug()),
async (message) => {
try {
const decodedPayload = decode_payload(message);
+4054 -69
View File
File diff suppressed because it is too large Load Diff