sharenet/frontend/src/lib/wasm.ts
continuist bb70355151
All checks were successful
Podman Rootless Demo / test-backend (push) Has been skipped
Podman Rootless Demo / test-frontend (push) Has been skipped
Podman Rootless Demo / build-backend (push) Has been skipped
Podman Rootless Demo / build-frontend (push) Successful in 9m4s
Podman Rootless Demo / deploy-prod (push) Successful in 55s
try to make wasm work
2025-10-26 11:03:00 -04:00

120 lines
No EOL
3.6 KiB
TypeScript

'use client';
import type { UserProfile, SPFPassport } from './auth/types';
/**
* WASM module interface with proper TypeScript typing
*/
interface PassportWASM {
parse_spf_file(data: Uint8Array, password: string): Promise<SPFPassport>;
get_profiles_from_passport(data: Uint8Array, password: string): Promise<UserProfile[]>;
validate_spf_signature(data: Uint8Array, signature: Uint8Array): Promise<boolean>;
}
/**
* WASM loader class for managing the WASM module
*/
export class PassportWASMLoader {
private module: PassportWASM | null = null;
private isLoading: boolean = false;
private loadPromise: Promise<PassportWASM> | null = null;
/**
* Initialize the WASM module
*/
async init(): Promise<PassportWASM> {
if (this.module) {
return this.module;
}
if (this.loadPromise) {
return this.loadPromise;
}
this.isLoading = true;
this.loadPromise = this.loadWASMModule();
try {
this.module = await this.loadPromise;
return this.module;
} catch (error) {
this.loadPromise = null;
this.isLoading = false;
throw error;
}
}
/**
* Load the WASM module dynamically
*/
private async loadWASMModule(): Promise<PassportWASM> {
if (typeof window === 'undefined') {
throw new Error('WASM module can only be loaded in browser environment');
}
try {
// Dynamically import the WASM bindings
// With bundler target, the module is automatically initialized on import
// but we need to ensure the WASM memory is ready before calling functions
const wasmModule = await import('./wasm-pkg/sharenet_passport_wasm');
// Test that the WASM module is properly initialized by checking if
// the wasm memory is accessible through a simple property access
// This ensures the WASM module is fully loaded before we use it
if (!wasmModule || typeof wasmModule.parse_spf_file !== 'function') {
throw new Error('WASM module exports not properly loaded');
}
// Create wrapper functions with proper typing
const wasmWrapper: PassportWASM = {
parse_spf_file: async (data: Uint8Array, password: string): Promise<SPFPassport> => {
const result = wasmModule.parse_spf_file(data, password);
// The WASM function returns a JsValue that we need to convert
// For now, we'll assume it returns the correct structure
return result as unknown as SPFPassport;
},
get_profiles_from_passport: async (data: Uint8Array, password: string): Promise<UserProfile[]> => {
const result = wasmModule.get_profiles_from_passport(data, password);
return result as unknown as UserProfile[];
},
validate_spf_signature: async (data: Uint8Array, signature: Uint8Array): Promise<boolean> => {
return wasmModule.validate_spf_signature(data, signature);
},
};
return wasmWrapper;
} catch (error) {
console.error('Failed to load WASM module:', error);
throw new Error(`Failed to load WASM module: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Check if the module is loaded
*/
isLoaded(): boolean {
return this.module !== null;
}
/**
* Check if the module is currently loading
*/
getIsLoading(): boolean {
return this.isLoading;
}
/**
* Get the loaded module (throws if not loaded)
*/
getModule(): PassportWASM {
if (!this.module) {
throw new Error('WASM module not loaded. Call init() first.');
}
return this.module;
}
}
// Create a singleton instance
export const passportWASM = new PassportWASMLoader();