smoldot 1.0.3 → 1.0.4

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.
@@ -22,6 +22,7 @@ export interface Config {
22
22
  onPanic: (message: string) => never;
23
23
  logCallback: (level: number, target: string, message: string) => void;
24
24
  jsonRpcResponsesNonEmptyCallback: (chainId: number) => void;
25
+ advanceExecutionReadyCallback: () => void;
25
26
  currentTaskCallback?: (taskName: string | null) => void;
26
27
  }
27
28
  /**
@@ -65,6 +65,9 @@ function default_1(config) {
65
65
  const buf = config.bufferIndices[bufferIndex];
66
66
  new Uint8Array(instance.exports.memory.buffer).set(buf, targetPtr);
67
67
  },
68
+ advance_execution_ready: () => {
69
+ config.advanceExecutionReadyCallback();
70
+ },
68
71
  // Used by the Rust side to notify that a JSON-RPC response or subscription notification
69
72
  // is available in the queue of JSON-RPC responses.
70
73
  json_rpc_responses_non_empty: (chainId) => {
@@ -90,7 +93,7 @@ function default_1(config) {
90
93
  }
91
94
  },
92
95
  // Must call `timer_finished` after the given number of milliseconds has elapsed.
93
- start_timer: (id, ms) => {
96
+ start_timer: (ms) => {
94
97
  if (killedTracked.killed)
95
98
  return;
96
99
  const instance = config.instance;
@@ -108,7 +111,7 @@ function default_1(config) {
108
111
  if (killedTracked.killed)
109
112
  return;
110
113
  try {
111
- instance.exports.timer_finished(id);
114
+ instance.exports.timer_finished();
112
115
  }
113
116
  catch (_error) { }
114
117
  });
@@ -118,7 +121,7 @@ function default_1(config) {
118
121
  if (killedTracked.killed)
119
122
  return;
120
123
  try {
121
- instance.exports.timer_finished(id);
124
+ instance.exports.timer_finished();
122
125
  }
123
126
  catch (_error) { }
124
127
  }, ms);
@@ -6,8 +6,8 @@
6
6
  */
7
7
  export interface SmoldotWasmExports extends WebAssembly.Exports {
8
8
  memory: WebAssembly.Memory;
9
- init: (maxLogLevel: number, enableCurrentTask: number, cpuRateLimit: number, periodicallyYield: number) => void;
10
- set_periodically_yield: (periodicallyYield: number) => void;
9
+ init: (maxLogLevel: number) => void;
10
+ advance_execution: () => number;
11
11
  start_shutdown: () => void;
12
12
  add_chain: (chainSpecBufferIndex: number, databaseContentBufferIndex: number, jsonRpcRunning: number, potentialRelayChainsBufferIndex: number) => number;
13
13
  remove_chain: (chainId: number) => void;
@@ -17,7 +17,7 @@ export interface SmoldotWasmExports extends WebAssembly.Exports {
17
17
  json_rpc_send: (textBufferIndex: number, chainId: number) => number;
18
18
  json_rpc_responses_peek: (chainId: number) => number;
19
19
  json_rpc_responses_pop: (chainId: number) => void;
20
- timer_finished: (timerId: number) => void;
20
+ timer_finished: () => void;
21
21
  connection_open_single_stream: (connectionId: number, handshakeTy: number, initialWritableBytes: number, writeClosable: number) => void;
22
22
  connection_open_multi_stream: (connectionId: number, handshakeTyBufferIndex: number) => void;
23
23
  stream_writable_bytes: (connectionId: number, streamId: number, numBytes: number) => void;
@@ -24,9 +24,9 @@ export declare class QueueFullError extends Error {
24
24
  * Contains the configuration of the instance.
25
25
  */
26
26
  export interface Config {
27
+ wasmModule: Promise<WebAssembly.Module>;
27
28
  logCallback: (level: number, target: string, message: string) => void;
28
29
  maxLogLevel: number;
29
- enableCurrentTask: boolean;
30
30
  cpuRateLimit: number;
31
31
  }
32
32
  export interface Instance {
@@ -69,65 +69,49 @@ function start(configMessage, platformBindings) {
69
69
  const printError = { printError: true };
70
70
  // Contains the information of each chain that is currently alive.
71
71
  let chains = new Map();
72
- // Start initialization of the Wasm VM.
73
- const config = {
74
- onWasmPanic: (message) => {
75
- // TODO: consider obtaining a backtrace here
76
- crashError.error = new CrashError(message);
77
- if (!printError.printError)
78
- return;
79
- console.error("Smoldot has panicked" +
80
- (currentTask.name ? (" while executing task `" + currentTask.name + "`") : "") +
81
- ". This is a bug in smoldot. Please open an issue at " +
82
- "https://github.com/smol-dot/smoldot/issues with the following message:\n" +
83
- message);
84
- for (const chain of Array.from(chains.values())) {
85
- for (const promise of chain.jsonRpcResponsesPromises) {
86
- promise.reject(crashError.error);
87
- }
88
- chain.jsonRpcResponsesPromises = [];
89
- }
90
- },
91
- logCallback: (level, target, message) => {
92
- configMessage.logCallback(level, target, message);
93
- },
94
- jsonRpcResponsesNonEmptyCallback: (chainId) => {
95
- // Notify every single promise found in `jsonRpcResponsesPromises`.
96
- const promises = chains.get(chainId).jsonRpcResponsesPromises;
97
- while (promises.length !== 0) {
98
- promises.shift().resolve();
99
- }
100
- },
101
- currentTaskCallback: (taskName) => {
102
- currentTask.name = taskName;
103
- },
104
- cpuRateLimit: configMessage.cpuRateLimit,
105
- };
106
- state = {
107
- initialized: false, promise: instance.startInstance(config, platformBindings).then(([instance, bufferIndices]) => {
108
- // `config.cpuRateLimit` is a floating point that should be between 0 and 1, while the value
109
- // to pass as parameter must be between `0` and `2^32-1`.
110
- // The few lines of code below should handle all possible values of `number`, including
111
- // infinites and NaN.
112
- let cpuRateLimit = Math.round(config.cpuRateLimit * 4294967295); // `2^32 - 1`
113
- if (cpuRateLimit < 0)
114
- cpuRateLimit = 0;
115
- if (cpuRateLimit > 4294967295)
116
- cpuRateLimit = 4294967295;
117
- if (!Number.isFinite(cpuRateLimit))
118
- cpuRateLimit = 4294967295; // User might have passed NaN
119
- // Smoldot requires an initial call to the `init` function in order to do its internal
120
- // configuration.
121
- const [periodicallyYield, unregisterCallback] = platformBindings.registerShouldPeriodicallyYield((newValue) => {
122
- if (state.initialized && !crashError.error) {
123
- try {
124
- state.instance.exports.set_periodically_yield(newValue ? 1 : 0);
72
+ const initPromise = (() => __awaiter(this, void 0, void 0, function* () {
73
+ const module = yield configMessage.wasmModule;
74
+ // Start initialization of the Wasm VM.
75
+ const config = {
76
+ onWasmPanic: (message) => {
77
+ // TODO: consider obtaining a backtrace here
78
+ crashError.error = new CrashError(message);
79
+ if (!printError.printError)
80
+ return;
81
+ console.error("Smoldot has panicked" +
82
+ (currentTask.name ? (" while executing task `" + currentTask.name + "`") : "") +
83
+ ". This is a bug in smoldot. Please open an issue at " +
84
+ "https://github.com/smol-dot/smoldot/issues with the following message:\n" +
85
+ message);
86
+ for (const chain of Array.from(chains.values())) {
87
+ for (const promise of chain.jsonRpcResponsesPromises) {
88
+ promise.reject(crashError.error);
125
89
  }
126
- catch (_error) { }
90
+ chain.jsonRpcResponsesPromises = [];
127
91
  }
128
- });
129
- instance.exports.init(configMessage.maxLogLevel, configMessage.enableCurrentTask ? 1 : 0, cpuRateLimit, periodicallyYield ? 1 : 0);
130
- state = { initialized: true, instance, bufferIndices, unregisterCallback };
92
+ },
93
+ logCallback: (level, target, message) => {
94
+ configMessage.logCallback(level, target, message);
95
+ },
96
+ wasmModule: module,
97
+ jsonRpcResponsesNonEmptyCallback: (chainId) => {
98
+ // Notify every single promise found in `jsonRpcResponsesPromises`.
99
+ const promises = chains.get(chainId).jsonRpcResponsesPromises;
100
+ while (promises.length !== 0) {
101
+ promises.shift().resolve();
102
+ }
103
+ },
104
+ currentTaskCallback: (taskName) => {
105
+ currentTask.name = taskName;
106
+ },
107
+ cpuRateLimit: configMessage.cpuRateLimit,
108
+ maxLogLevel: configMessage.maxLogLevel,
109
+ };
110
+ return yield instance.startInstance(config, platformBindings);
111
+ }))();
112
+ state = {
113
+ initialized: false, promise: initPromise.then(([instance, bufferIndices]) => {
114
+ state = { initialized: true, instance, bufferIndices };
131
115
  return [instance, bufferIndices];
132
116
  })
133
117
  };
@@ -280,8 +264,6 @@ function start(configMessage, platformBindings) {
280
264
  // exception when the user wants the shutdown to happen.
281
265
  if (crashError.error)
282
266
  return;
283
- if (state.initialized)
284
- state.unregisterCallback();
285
267
  try {
286
268
  printError.printError = false;
287
269
  instance.exports.start_shutdown();
@@ -15,25 +15,16 @@ export interface Config {
15
15
  */
16
16
  onWasmPanic: (message: string) => void;
17
17
  logCallback: (level: number, target: string, message: string) => void;
18
+ wasmModule: WebAssembly.Module;
18
19
  jsonRpcResponsesNonEmptyCallback: (chainId: number) => void;
19
20
  currentTaskCallback?: (taskName: string | null) => void;
21
+ maxLogLevel: number;
20
22
  cpuRateLimit: number;
21
23
  }
22
24
  /**
23
25
  * Contains functions that the client will use when it needs to leverage the platform.
24
26
  */
25
27
  export interface PlatformBindings {
26
- /**
27
- * Base64-decode the given buffer then decompress its content using the inflate algorithm
28
- * with zlib header.
29
- *
30
- * The input is considered trusted. In other words, the implementation doesn't have to
31
- * resist malicious input.
32
- *
33
- * This function is asynchronous because implementations might use the compression streams
34
- * Web API, which for whatever reason is asynchronous.
35
- */
36
- trustedBase64DecodeAndZlibInflate: (input: string) => Promise<Uint8Array>;
37
28
  /**
38
29
  * Returns the number of milliseconds since an arbitrary epoch.
39
30
  */
@@ -25,23 +25,23 @@ exports.startInstance = exports.ConnectionError = void 0;
25
25
  // along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
  const bindings_smoldot_light_js_1 = require("./bindings-smoldot-light.js");
27
27
  const bindings_wasi_js_1 = require("./bindings-wasi.js");
28
- const wasm_js_1 = require("./autogen/wasm.js");
29
28
  var bindings_smoldot_light_js_2 = require("./bindings-smoldot-light.js");
30
29
  Object.defineProperty(exports, "ConnectionError", { enumerable: true, get: function () { return bindings_smoldot_light_js_2.ConnectionError; } });
31
30
  function startInstance(config, platformBindings) {
32
31
  return __awaiter(this, void 0, void 0, function* () {
33
- // The actual Wasm bytecode is base64-decoded then deflate-decoded from a constant found in a
34
- // different file.
35
- // This is suboptimal compared to using `instantiateStreaming`, but it is the most
36
- // cross-platform cross-bundler approach.
37
- const wasmBytecode = yield platformBindings.trustedBase64DecodeAndZlibInflate(wasm_js_1.default);
38
32
  let killAll;
39
33
  const bufferIndices = new Array;
34
+ // Callback called when `advance_execution_ready` is called by the Rust code, if any.
35
+ const advanceExecutionPromise = { value: null };
40
36
  // Used to bind with the smoldot-light bindings. See the `bindings-smoldot-light.js` file.
41
37
  const smoldotJsConfig = Object.assign({ bufferIndices, connect: platformBindings.connect, onPanic: (message) => {
42
38
  killAll();
43
39
  config.onWasmPanic(message);
44
40
  throw new Error();
41
+ }, advanceExecutionReadyCallback: () => {
42
+ if (advanceExecutionPromise.value)
43
+ advanceExecutionPromise.value();
44
+ advanceExecutionPromise.value = null;
45
45
  } }, config);
46
46
  // Used to bind with the Wasi bindings. See the `bindings-wasi.js` file.
47
47
  const wasiConfig = {
@@ -59,15 +59,70 @@ function startInstance(config, platformBindings) {
59
59
  // Start the Wasm virtual machine.
60
60
  // The Rust code defines a list of imports that must be fulfilled by the environment. The second
61
61
  // parameter provides their implementations.
62
- const result = yield WebAssembly.instantiate(wasmBytecode, {
62
+ const result = yield WebAssembly.instantiate(config.wasmModule, {
63
63
  // The functions with the "smoldot" prefix are specific to smoldot.
64
64
  "smoldot": smoldotBindings,
65
65
  // As the Rust code is compiled for wasi, some more wasi-specific imports exist.
66
66
  "wasi_snapshot_preview1": (0, bindings_wasi_js_1.default)(wasiConfig),
67
67
  });
68
- const instance = result.instance;
68
+ const instance = result;
69
69
  smoldotJsConfig.instance = instance;
70
70
  wasiConfig.instance = instance;
71
+ // Smoldot requires an initial call to the `init` function in order to do its internal
72
+ // configuration.
73
+ instance.exports.init(config.maxLogLevel);
74
+ (() => __awaiter(this, void 0, void 0, function* () {
75
+ // In order to avoid calling `setTimeout` too often, we accumulate sleep up until
76
+ // a certain threshold.
77
+ let missingSleep = 0;
78
+ // Extract (to make sure the value doesn't change) and sanitize `cpuRateLimit`.
79
+ let cpuRateLimit = config.cpuRateLimit;
80
+ if (isNaN(cpuRateLimit))
81
+ cpuRateLimit = 1.0;
82
+ if (cpuRateLimit > 1.0)
83
+ cpuRateLimit = 1.0;
84
+ if (cpuRateLimit < 0.0)
85
+ cpuRateLimit = 0.0;
86
+ const periodicallyYield = { value: false };
87
+ const [periodicallyYieldInit, unregisterCallback] = platformBindings.registerShouldPeriodicallyYield((newValue) => {
88
+ periodicallyYield.value = newValue;
89
+ });
90
+ periodicallyYield.value = periodicallyYieldInit;
91
+ let now = platformBindings.performanceNow();
92
+ while (true) {
93
+ const whenReadyAgain = new Promise((resolve) => advanceExecutionPromise.value = resolve);
94
+ const outcome = instance.exports.advance_execution();
95
+ if (outcome === 0) {
96
+ unregisterCallback();
97
+ break;
98
+ }
99
+ const afterExec = platformBindings.performanceNow();
100
+ const elapsed = afterExec - now;
101
+ now = afterExec;
102
+ // In order to enforce the rate limiting, we stop executing for a certain
103
+ // amount of time.
104
+ // The base equation here is: `(sleep + elapsed) * rateLimit == elapsed`,
105
+ // from which the calculation below is derived.
106
+ const sleep = elapsed * (1.0 / cpuRateLimit - 1.0);
107
+ missingSleep += sleep;
108
+ if (missingSleep > (periodicallyYield ? 5 : 1000)) {
109
+ // `setTimeout` has a maximum value, after which it will overflow. 🤦
110
+ // See <https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value>
111
+ // While adding a cap technically skews the CPU rate limiting algorithm, we don't
112
+ // really care for such extreme values.
113
+ if (missingSleep > 2147483646) // Doc says `> 2147483647`, but I don't really trust their pedanticism so let's be safe
114
+ missingSleep = 2147483646;
115
+ yield new Promise((resolve) => setTimeout(resolve, missingSleep));
116
+ missingSleep = 0;
117
+ }
118
+ yield whenReadyAgain;
119
+ const afterWait = platformBindings.performanceNow();
120
+ missingSleep -= (afterWait - now);
121
+ if (missingSleep < 0)
122
+ missingSleep = 0;
123
+ now = afterWait;
124
+ }
125
+ }))();
71
126
  return [instance, bufferIndices];
72
127
  });
73
128
  }
@@ -317,4 +317,4 @@ export interface AddChainOptions {
317
317
  */
318
318
  disableJsonRpc?: boolean;
319
319
  }
320
- export declare function start(options: ClientOptions, platformBindings: PlatformBindings): Client;
320
+ export declare function start(options: ClientOptions, wasmModule: Promise<WebAssembly.Module>, platformBindings: PlatformBindings): Client;
@@ -53,7 +53,7 @@ export class JsonRpcDisabledError extends Error {
53
53
  // This function is similar to the `start` function found in `index.ts`, except with an extra
54
54
  // parameter containing the platform-specific bindings.
55
55
  // Contrary to the one within `index.js`, this function is not supposed to be directly used.
56
- export function start(options, platformBindings) {
56
+ export function start(options, wasmModule, platformBindings) {
57
57
  const logCallback = options.logCallback || ((level, target, message) => {
58
58
  // The first parameter of the methods of `console` has some printf-like substitution
59
59
  // capabilities. We don't really need to use this, but not using it means that the logs might
@@ -82,14 +82,11 @@ export function start(options, platformBindings) {
82
82
  // All the functions of the public API check if this contains a value.
83
83
  const alreadyDestroyedError = { value: null };
84
84
  const instance = startInstance({
85
+ wasmModule,
85
86
  // Maximum level of log entries sent by the client.
86
87
  // 0 = Logging disabled, 1 = Error, 2 = Warn, 3 = Info, 4 = Debug, 5 = Trace
87
88
  maxLogLevel: options.maxLogLevel || 3,
88
89
  logCallback,
89
- // `enableCurrentTask` adds a small performance hit, but adds some additional information to
90
- // crash reports. Whether this should be enabled is very opiniated and not that important. At
91
- // the moment, we enable it all the time, except if the user has logging disabled altogether.
92
- enableCurrentTask: options.maxLogLevel ? options.maxLogLevel >= 1 : true,
93
90
  cpuRateLimit: options.cpuRateLimit || 1.0,
94
91
  }, platformBindings);
95
92
  return {
@@ -25,6 +25,7 @@ import { start as innerStart } from './client.js';
25
25
  import { ConnectionError } from './instance/instance.js';
26
26
  import { classicDecode, multibaseBase64Decode } from './base64.js';
27
27
  import { inflate } from 'pako';
28
+ import { default as wasmBase64 } from './instance/autogen/wasm.js';
28
29
  export { AddChainError, AlreadyDestroyedError, CrashError, JsonRpcDisabledError, MalformedJsonRpcError, QueueFullError } from './client.js';
29
30
  /**
30
31
  * Initializes a new client. This is a pre-requisite to connecting to a blockchain.
@@ -35,10 +36,12 @@ export { AddChainError, AlreadyDestroyedError, CrashError, JsonRpcDisabledError,
35
36
  */
36
37
  export function start(options) {
37
38
  options = options || {};
38
- return innerStart(options, {
39
- trustedBase64DecodeAndZlibInflate: (input) => {
40
- return Promise.resolve(inflate(classicDecode(input)));
41
- },
39
+ // The actual Wasm bytecode is base64-decoded then deflate-decoded from a constant found in a
40
+ // different file.
41
+ // This is suboptimal compared to using `instantiateStreaming`, but it is the most
42
+ // cross-platform cross-bundler approach.
43
+ const wasmModule = WebAssembly.compile(inflate(classicDecode(wasmBase64)));
44
+ return innerStart(options, wasmModule, {
42
45
  registerShouldPeriodicallyYield: (callback) => {
43
46
  if (typeof document === 'undefined') // We might be in a web worker.
44
47
  return [false, () => { }];
@@ -53,7 +56,16 @@ export function start(options) {
53
56
  const crypto = globalThis.crypto;
54
57
  if (!crypto)
55
58
  throw new Error('randomness not available');
56
- crypto.getRandomValues(buffer);
59
+ // Browsers have this completely undocumented behavior (it's not even part of a spec)
60
+ // that for some reason `getRandomValues` can't be called on arrayviews back by
61
+ // `SharedArrayBuffer`s and they throw an exception if you try.
62
+ if (buffer.buffer instanceof ArrayBuffer)
63
+ crypto.getRandomValues(buffer);
64
+ else {
65
+ const tmpArray = new Uint8Array(buffer.length);
66
+ crypto.getRandomValues(tmpArray);
67
+ buffer.set(tmpArray);
68
+ }
57
69
  },
58
70
  connect: (config) => {
59
71
  return connect(config, (options === null || options === void 0 ? void 0 : options.forbidWs) || false, (options === null || options === void 0 ? void 0 : options.forbidNonLocalWs) || false, (options === null || options === void 0 ? void 0 : options.forbidWss) || false, (options === null || options === void 0 ? void 0 : options.forbidWebRtc) || false);
@@ -22,6 +22,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
22
22
  // along with this program. If not, see <http://www.gnu.org/licenses/>.
23
23
  import { start as innerStart } from './client.js';
24
24
  import { ConnectionError } from './instance/instance.js';
25
+ import { default as wasmBase64 } from './instance/autogen/wasm.js';
25
26
  export { AddChainError, AlreadyDestroyedError, CrashError, MalformedJsonRpcError, QueueFullError, JsonRpcDisabledError } from './client.js';
26
27
  /**
27
28
  * Initializes a new client. This is a pre-requisite to connecting to a blockchain.
@@ -32,33 +33,12 @@ export { AddChainError, AlreadyDestroyedError, CrashError, MalformedJsonRpcError
32
33
  */
33
34
  export function start(options) {
34
35
  options = options || {};
35
- return innerStart(options || {}, {
36
- trustedBase64DecodeAndZlibInflate: (input) => __awaiter(this, void 0, void 0, function* () {
37
- const buffer = trustedBase64Decode(input);
38
- // This code has been copy-pasted from the official streams draft specification.
39
- // At the moment, it is found here: https://wicg.github.io/compression/#example-deflate-compress
40
- const ds = new DecompressionStream('deflate');
41
- const writer = ds.writable.getWriter();
42
- writer.write(buffer);
43
- writer.close();
44
- const output = [];
45
- const reader = ds.readable.getReader();
46
- let totalSize = 0;
47
- while (true) {
48
- const { value, done } = yield reader.read();
49
- if (done)
50
- break;
51
- output.push(value);
52
- totalSize += value.byteLength;
53
- }
54
- const concatenated = new Uint8Array(totalSize);
55
- let offset = 0;
56
- for (const array of output) {
57
- concatenated.set(array, offset);
58
- offset += array.byteLength;
59
- }
60
- return concatenated;
61
- }),
36
+ // The actual Wasm bytecode is base64-decoded then deflate-decoded from a constant found in a
37
+ // different file.
38
+ // This is suboptimal compared to using `instantiateStreaming`, but it is the most
39
+ // cross-platform cross-bundler approach.
40
+ const wasmModule = zlibInflate(trustedBase64Decode(wasmBase64)).then(((bytecode) => WebAssembly.compile(bytecode)));
41
+ return innerStart(options || {}, wasmModule, {
62
42
  registerShouldPeriodicallyYield: (_callback) => {
63
43
  return [true, () => { }];
64
44
  },
@@ -76,6 +56,36 @@ export function start(options) {
76
56
  }
77
57
  });
78
58
  }
59
+ /**
60
+ * Applies the zlib inflate algorithm on the buffer.
61
+ */
62
+ function zlibInflate(buffer) {
63
+ return __awaiter(this, void 0, void 0, function* () {
64
+ // This code has been copy-pasted from the official streams draft specification.
65
+ // At the moment, it is found here: https://wicg.github.io/compression/#example-deflate-compress
66
+ const ds = new DecompressionStream('deflate');
67
+ const writer = ds.writable.getWriter();
68
+ writer.write(buffer);
69
+ writer.close();
70
+ const output = [];
71
+ const reader = ds.readable.getReader();
72
+ let totalSize = 0;
73
+ while (true) {
74
+ const { value, done } = yield reader.read();
75
+ if (done)
76
+ break;
77
+ output.push(value);
78
+ totalSize += value.byteLength;
79
+ }
80
+ const concatenated = new Uint8Array(totalSize);
81
+ let offset = 0;
82
+ for (const array of output) {
83
+ concatenated.set(array, offset);
84
+ offset += array.byteLength;
85
+ }
86
+ return concatenated;
87
+ });
88
+ }
79
89
  /**
80
90
  * Decodes a base64 string.
81
91
  *
@@ -227,7 +237,7 @@ function connect(config, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss) {
227
237
  config.onMessage(readBuffer.slice(0, outcome));
228
238
  return read(readBuffer);
229
239
  });
230
- read(new Uint8Array(1024));
240
+ read(new Uint8Array(32768));
231
241
  return established;
232
242
  });
233
243
  return {
@@ -16,6 +16,7 @@
16
16
  // with both at the same time.
17
17
  import { start as innerStart } from './client.js';
18
18
  import { ConnectionError } from './instance/instance.js';
19
+ import { default as wasmBase64 } from './instance/autogen/wasm.js';
19
20
  import { WebSocket } from 'ws';
20
21
  import { inflate } from 'pako';
21
22
  import { performance } from 'node:perf_hooks';
@@ -31,10 +32,12 @@ export { AddChainError, AlreadyDestroyedError, CrashError, MalformedJsonRpcError
31
32
  */
32
33
  export function start(options) {
33
34
  options = options || {};
34
- return innerStart(options || {}, {
35
- trustedBase64DecodeAndZlibInflate: (input) => {
36
- return Promise.resolve(inflate(Buffer.from(input, 'base64')));
37
- },
35
+ // The actual Wasm bytecode is base64-decoded then deflate-decoded from a constant found in a
36
+ // different file.
37
+ // This is suboptimal compared to using `instantiateStreaming`, but it is the most
38
+ // cross-platform cross-bundler approach.
39
+ const wasmModule = WebAssembly.compile(inflate(Buffer.from(wasmBase64, 'base64')));
40
+ return innerStart(options || {}, wasmModule, {
38
41
  registerShouldPeriodicallyYield: (_callback) => {
39
42
  return [true, () => { }];
40
43
  },