summaryrefslogtreecommitdiff
path: root/crates/chat-crypto/browser/wasi.ts
diff options
context:
space:
mode:
authorDoan Luu Duc Tai <doan@luuduct.ai>2026-10-31 13:08:08 -0500
committerDoan Luu Duc Tai <doan@luuduct.ai>2026-10-31 13:08:08 -0500
commita80df869a92c330fbad9695057720b11a18af901 (patch)
tree5307272821cb3adc3f0348413a3b904290890044 /crates/chat-crypto/browser/wasi.ts
parent9139398d465f5899cc31bc4cdd54daa7392ca550 (diff)
Forbid Rust lints and enforce strict typed TypeScript linting
Diffstat (limited to 'crates/chat-crypto/browser/wasi.ts')
-rw-r--r--crates/chat-crypto/browser/wasi.ts27
1 files changed, 16 insertions, 11 deletions
diff --git a/crates/chat-crypto/browser/wasi.ts b/crates/chat-crypto/browser/wasi.ts
index a037519..f2d9290 100644
--- a/crates/chat-crypto/browser/wasi.ts
+++ b/crates/chat-crypto/browser/wasi.ts
@@ -15,8 +15,12 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
+function isStart(value: WebAssembly.ExportValue | undefined): value is () => unknown {
+ return typeof value === 'function' && value.length === 0;
+}
+
async function execute(bytes: ArrayBuffer, input: string): Promise<StackChat.Execution> {
- let memory: WebAssembly.Memory | undefined;
+ const instanceState: { memory: WebAssembly.Memory | undefined } = { memory: undefined };
let output = '';
let errors = '';
const stdoutDecoder = new TextDecoder('utf-8', { fatal: true });
@@ -26,15 +30,16 @@ async function execute(bytes: ArrayBuffer, input: string): Promise<StackChat.Exe
let randomCalls = 0;
let randomBytes = 0;
const memoryBuffer = (): ArrayBuffer => {
- if (!memory) throw new Error('WASI memory is unavailable');
+ const memory = instanceState.memory;
+ if (memory === undefined) throw new Error('WASI memory is unavailable');
return memory.buffer;
};
const data = (): DataView => new DataView(memoryBuffer());
- const put32 = (address: number, value: number) => data().setUint32(address, value, true);
- const noEntries = (count: number, size: number) => { put32(count, 0); put32(size, 0); return 0; };
+ const put32 = (address: number, value: number): void => { data().setUint32(address, value, true); };
+ const noEntries = (count: number, size: number): number => { put32(count, 0); put32(size, 0); return 0; };
class Exit extends Error {
readonly code: number;
- constructor(code: number) { super(`WASI exit ${code}`); this.code = code; }
+ constructor(code: number) { super(`WASI exit ${String(code)}`); this.code = code; }
}
const wasi: Record<string, WebAssembly.ImportValue> = {
args_sizes_get: noEntries,
@@ -108,7 +113,7 @@ async function execute(bytes: ArrayBuffer, input: string): Promise<StackChat.Exe
'path_filestat_get', 'path_filestat_set_times', 'path_readlink', 'path_unlink_file',
'path_rename', 'path_create_directory', 'path_remove_directory', 'path_link', 'path_symlink',
'sock_accept', 'sock_recv', 'sock_send', 'sock_shutdown', 'poll_oneoff',
- ]) wasi[name] = () => 76;
+ ]) wasi[name] = (): number => 76;
const module = await WebAssembly.compile(bytes);
for (const entry of WebAssembly.Module.imports(module)) {
if (entry.module !== 'wasi_snapshot_preview1' || typeof wasi[entry.name] !== 'function') {
@@ -118,17 +123,17 @@ async function execute(bytes: ArrayBuffer, input: string): Promise<StackChat.Exe
const instance = await WebAssembly.instantiate(module, { wasi_snapshot_preview1: wasi });
const exportedMemory = instance.exports['memory'];
const start = instance.exports['_start'];
- if (!(exportedMemory instanceof WebAssembly.Memory) || typeof start !== 'function') throw new Error('Invalid WASI exports');
- memory = exportedMemory;
+ if (!(exportedMemory instanceof WebAssembly.Memory) || !isStart(start)) throw new Error('Invalid WASI exports');
+ instanceState.memory = exportedMemory;
try { start(); }
- catch (error) { if (!(error instanceof Exit) || error.code !== 0) throw new Error(`${error instanceof Error ? error.message : 'WASI execution failed'}: ${errors}`); }
+ catch (error) { if (!(error instanceof Exit) || error.code !== 0) throw new Error(`${error instanceof Error ? error.message : 'WASI execution failed'}: ${errors}`, { cause: error }); }
output += stdoutDecoder.decode();
errors += stderrDecoder.decode();
stdin.fill(0);
return { output, randomCalls, randomBytes };
}
-async function runProbe(bytes: ArrayBuffer) {
+async function runProbe(bytes: ArrayBuffer): Promise<StackChat.Execution> {
const result = await execute(bytes, '');
if (!result.output.startsWith('PASS: backend=OpenSSL ') || result.randomCalls === 0) {
throw new Error('OpenSSL probe did not pass');
@@ -146,6 +151,6 @@ async function runCommand(bytes: ArrayBuffer, request: StackChat.CryptoRequest):
return response;
}
-globalThis.stackChatWasi = Object.freeze({ runCommand, runProbe });
+Object.defineProperty(globalThis, 'stackChatWasi', { value: Object.freeze({ runCommand, runProbe }), writable: false, configurable: false });
// @license-end