Skip to content

Browser-Specific Standard Library Implementations (WebCrypto, Fetch, OPFS, Web Workers) #175

@mjm918

Description

@mjm918

Browser-Specific Standard Library Implementations

Problem

naml's standard library crates are built on native OS APIs (libc, std::fs, std::net, tokio, etc.) that don't exist in browser WASM environments. For naml to run in browsers, each std module needs a browser-specific implementation using Web APIs, selectable via the #[platforms(browser)] attribute system.

Current Std Library Platform Analysis

Already WASM-Compatible (no changes needed)

These crates use pure Rust with no OS dependencies:

  • naml-std-core — HeapHeader, NamlString, NamlStruct, refcounting
  • naml-std-random — Can use getrandom crate (supports wasm32)
  • naml-std-datetime — Needs js_sys::Date for browser, otherwise pure Rust
  • naml-std-metricsperformance.now() via web_sys for browser timing
  • naml-std-strings — Pure string operations
  • naml-std-collections — Pure data structure operations
  • naml-std-path — Pure path manipulation
  • naml-std-testing — Pure assertion logic
  • naml-std-encoding — JSON/TOML/YAML/Base64/Hex are pure Rust

Need Browser Implementations

Each of these needs a #[platforms(browser)] alternative:

Module Native API Browser API Complexity
std::crypto RustCrypto crates WebCrypto API (crypto.subtle) Medium
std::net::http Hyper + Tokio Fetch API (fetch()) Medium
std::fs std::fs + libc OPFS (Origin Private File System) High
std::threads OS threads (M:N scheduler) Web Workers + SharedArrayBuffer Very High
std::io libc terminal control DOM Console API Low

Native-Only (no browser equivalent)

These modules have no meaningful browser equivalent and should be excluded:

  • std::process — fork/exec/signals don't exist in browsers
  • std::os — uid/gid/hostname are OS concepts
  • std::sqlite — Could use sql.js/wa-sqlite, but very different API surface

Proposed Browser Implementations

1. std::crypto → WebCrypto API

// Same naml API, different implementation
var hash: bytes = sha256(data);        // crypto.subtle.digest("SHA-256", data)
var mac: bytes = hmac_sha256(key, data); // crypto.subtle.sign("HMAC", key, data)
var key: bytes = pbkdf2_sha256(...);   // crypto.subtle.deriveBits("PBKDF2", ...)
var rand: bytes = random_bytes(32);    // crypto.getRandomValues(new Uint8Array(32))

Implementation: Import WebCrypto functions via wasm-bindgen. Note that WebCrypto is async — naml's sync API would need to use wasm-bindgen-futures or block on promises.

Challenge: WebCrypto's crypto.subtle.digest() returns a Promise<ArrayBuffer>, but naml's sha256() is synchronous. Options:

  • Use synchronous WebCrypto where available (only getRandomValues is sync)
  • Compile RustCrypto to WASM directly (works, just larger binary)
  • Make naml's crypto API async-aware on browser target

2. std::net::http → Fetch API

// Same naml API
var resp: response = http_get("https://api.example.com/data", none);

Native: Hyper + Tokio (current implementation)
Browser: fetch() API via web_sys::window().fetch()

Implementation:

  • wasm-bindgen + web-sys with fetch feature
  • Map naml's request/response structs to web_sys::Request/web_sys::Response
  • Handle async nature: Fetch returns Promises, needs wasm-bindgen-futures

HTTP Server: Not applicable in browser — serve() should be platform-gated to native/server only.

3. std::fs → Origin Private File System (OPFS)

// Same naml API
var content: string = read_file("data/config.toml");
write_file("output/result.txt", content);

Browser: Use OPFS (navigator.storage.getDirectory())

  • Provides a sandboxed filesystem in the browser
  • Supports read/write/delete operations
  • Async API — same challenge as WebCrypto

Limitations:

  • No access to real filesystem (sandboxed)
  • Different path semantics (no absolute paths)
  • Size quotas apply

4. std::threads → Web Workers

This is the most complex adaptation.

Native: Custom M:N scheduler with OS threads, channels, spawn blocks
Browser: Web Workers with postMessage for communication

Challenges:

  • Web Workers are isolated — no shared memory by default
  • SharedArrayBuffer enables shared memory but requires COOP/COEP headers
  • naml's channel semantics need mapping to postMessage/MessageChannel
  • naml's spawn {} blocks capture variables — Web Workers can't share JS heap objects
  • Atomics (std::sync::atomic) work with SharedArrayBuffer in WASM

Possible approaches:

  1. SharedArrayBuffer + Atomics: Closest to native semantics, requires secure context
  2. postMessage serialization: Simpler but no shared state, channels become message passing
  3. Single-threaded async: Use Promise/async instead of real parallelism

5. std::io → Console API

// Native: libc terminal control
// Browser: console.log, prompt()
println("Hello");           // console.log("Hello")
var input: string = readln(); // prompt("") or custom DOM input

Simple mapping — mostly console.log/console.error. Interactive input (read_key, readln) would need DOM integration.

Architecture: How to Structure Platform-Specific Code

Option A: Conditional compilation in Rust (recommended for std crates)

#[cfg(not(target_arch = "wasm32"))]
mod native;
#[cfg(target_arch = "wasm32")]
mod browser;

// Re-export the correct implementation
#[cfg(not(target_arch = "wasm32"))]
pub use native::*;
#[cfg(target_arch = "wasm32")]
pub use browser::*;

Option B: Separate crates

std/naml-std-crypto/         → Pure Rust (works everywhere)
std/naml-std-crypto-browser/ → WebCrypto wasm-bindgen wrapper

Option C: Feature flags

[features]
native = ["tokio", "hyper"]
browser = ["wasm-bindgen", "web-sys", "js-sys"]

Files to Create

For each browser-adapted module:

  • std/naml-std-<module>/src/browser.rs — Browser-specific implementation
  • Or std/naml-std-<module>-browser/ — Separate browser crate

Files to Modify

  • std/naml-std-crypto/Cargo.toml — Add wasm-bindgen, web-sys as optional deps
  • std/naml-std-net/Cargo.toml — Add web-sys fetch feature
  • std/naml-std-fs/Cargo.toml — Add OPFS support
  • namlc/src/codegen/cranelift/mod.rs — Register browser-specific runtime symbols
  • namlc/src/typechecker/mod.rs — Platform-aware function registration

Acceptance Criteria

  • std::crypto functions work in browser WASM using WebCrypto or compiled RustCrypto
  • std::net::http client functions work in browser using Fetch API
  • std::fs provides sandboxed file operations via OPFS in browser
  • std::threads::spawn maps to Web Workers (or provides clear async alternative)
  • std::io::println maps to console.log in browser
  • Native-only modules (std::process, std::os) produce clear compile errors when targeting browser
  • Same naml source code works on both native and browser with no changes (where APIs overlap)

Depends On

  • AOT Compilation Backend (need to compile to WASM)
  • naml build --target browser (need build pipeline)
  • Platform Attribute Enforcement (need to select correct implementation)

Blocks

  • Running naml applications in web browsers

Metadata

Metadata

Assignees

No one assigned

    Labels

    futureLong-term roadmap items

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions