Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @ts-check
import init, { LogLevel } from "../kdf/bin/kdflib.js";
import * as kdflib from "../kdf/bin/kdflib.js";
import { loadCompressedWasm } from "./wasm_loader.js";

const LOG_LEVEL = LogLevel.Info;

Expand Down Expand Up @@ -42,7 +43,11 @@ kdf.init_wasm = async function () {
}

kdf._isInitializing = true;
kdf._initPromise = init()
const gzipWasmBinPath = "../kdf/bin/kdflib_bg.wasm.gz"
const gzipKdfBinUrl = new URL(gzipWasmBinPath, import.meta.url);
const kdfBinBuffer = await loadCompressedWasm(gzipKdfBinUrl);

Comment on lines 45 to +49
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reset init flag when compressed wasm fetch fails

If loadCompressedWasm throws (e.g., missing kdflib_bg.wasm.gz or no DecompressionStream support), the error occurs before _initPromise is assigned, so _isInitializing remains true and no catch handler clears it. Subsequent init_wasm calls hit the _isInitializing branch that polls for _initPromise and will wait forever, leaving the KDF unusable after a single fetch failure.

Useful? React with 👍 / 👎.

kdf._initPromise = init(kdfBinBuffer)
.then(() => {
kdf._isInitializing = false;
kdf._initPromise = null;
Expand Down
66 changes: 66 additions & 0 deletions packages/komodo_defi_framework/web/res/wasm_loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
export async function loadCompressedWasm(url) {
try {
// Fetch the compressed WASM file
const response = await fetch(url.toString(), {
method: 'GET',
cache: 'force-cache', // Use cache if available
});

if (!response.ok) {
throw new Error(
`Failed to fetch compressed WASM: ${response.status} ${response.statusText}`
);
}

// Check if browser supports DecompressionStream
if (!('DecompressionStream' in globalThis)) {
throw new Error(
'Browser does not support DecompressionStream API. Please use a modern browser.'
);
}

// Ensure response.body is available
if (!response.body) {
throw new Error(
'Response body is not available. Streaming not supported or body already consumed.'
);
}

// Create a decompression stream for gzip
const decompressionStream = new DecompressionStream('gzip');

// Pipe the response through the decompression stream
const decompressedStream = response.body.pipeThrough(decompressionStream);

// Read the decompressed stream into an ArrayBuffer
const reader = decompressedStream.getReader();
const chunks = [];

while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}

// Combine all chunks into a single ArrayBuffer
const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
const result = new Uint8Array(totalLength);
let offset = 0;

for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}

//console.log(`WASM decompressed: ${(totalLength / 1024 / 1024).toFixed(2)} MB`);

return result.buffer;
} catch (error) {
console.error('Failed to load compressed WASM:', error);
throw error;
}
}

export function supportsGzipDecompression() {
return 'DecompressionStream' in globalThis;
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ class CopyPlatformAssetsBuildStep extends BuildStep {
await for (final entity in sourceDir.list(recursive: true)) {
if (entity is File) {
final relativePath = path.relative(entity.path, from: sourceDir.path);
final destFile = File(path.join(destDir.path, relativePath));

if (skipDir != null &&
path.isWithin(
Expand All @@ -151,13 +150,44 @@ class CopyPlatformAssetsBuildStep extends BuildStep {
continue;
}

if (!destFile.parent.existsSync()) {
destFile.parent.createSync(recursive: true);
// Check if this is a WASM file that needs compression
if (path.basename(entity.path) == 'kdflib_bg.wasm') {
// Compress and save as .wasm.gz
final destFile = File(path.join(destDir.path, relativePath + '.gz'));

if (!destFile.parent.existsSync()) {
destFile.parent.createSync(recursive: true);
}
if (destFile.existsSync()) {
destFile.deleteSync();
}

// Read, compress, and write
final wasmBytes = await entity.readAsBytes();
final originalSize = wasmBytes.length;
final compressedBytes = gzip.encode(wasmBytes);
Comment on lines +165 to +168
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add gzip import for wasm compression

The new compression branch calls gzip.encode(wasmBytes) but this file only imports dart:io; the gzip codec is defined in dart:convert, so this will not compile (Undefined name 'gzip') and the build step fails before copying assets. Import the proper codec or qualify it correctly.

Useful? React with 👍 / 👎.

final compressedSize = compressedBytes.length;
await destFile.writeAsBytes(compressedBytes);

final compressionRatio =
((originalSize - compressedSize) / originalSize * 100).toStringAsFixed(1);
_log.info(
'Compressed kdflib_bg.wasm during copy: '
'${originalSize} bytes -> ${compressedSize} bytes '
'($compressionRatio% reduction)',
);
} else {
// Normal file copy
final destFile = File(path.join(destDir.path, relativePath));

if (!destFile.parent.existsSync()) {
destFile.parent.createSync(recursive: true);
}
if (destFile.existsSync()) {
destFile.deleteSync();
}
entity.copySync(destFile.path);
}
if (destFile.existsSync()) {
destFile.deleteSync();
}
entity.copySync(destFile.path);
}
}
_log.info(
Expand Down