Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
@thi.ng/wasm-api-dom
Advanced tools
Browser DOM bridge API for hybrid TypeScript & WASM (Zig) applications
This project is part of the @thi.ng/umbrella monorepo.
Browser DOM bridge API for hybrid TypeScript & WASM (Zig) applications. This is a support package for @thi.ng/wasm-api.
This package provides a minimal, but already quite usable TypeScript core API and related Ziglang bindings for UI & DOM creation/manipulation via WebAssembly.
Current key features for the Zig (WASM) side:
.innerHTML
& .innerText
settersBefore the Zig WASM API module can be used, it must be initialized with a
standard std.mem.Allocator
. The currently recommended pattern looks something
like this:
const std = @import("std");
const wasm = @import("wasmapi");
const dom = @import("dom");
// expose thi.ng/wasm-api core API (incl. panic handler & allocation fns)
pub usingnamespace wasm;
// allocator, also exposed & used by JS-side WasmBridge & DOM module
// see further comments in:
// https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/zig/lib.zig
// https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api-dom/zig/events.zig
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
pub const WASM_ALLOCATOR = gpa.allocator();
/// Since various initialization functions can return errors
/// we're bundling them all in a single fn, which is then called by start()
/// and so only needs one code site for error handling
fn init() !void {
// the DOM API module must always be intialized first!
try dom.init(WASM_ALLOCATOR);
// ...
}
/// Main entry point
export fn start() void {
init() catch |e| @panic(@errorName(e));
}
Since DOM related resources created on the JS side cannot be returned to the WASM module directly, the bridge API caches those on the host side and uses managed ID (integer) handles to exchange them. These IDs can then be used in subsequent API calls to refer to certain DOM elements, listeners etc.
For element & event related functionality the following IDs are reserved:
-1
the browser window
itself0
document.head
1
document.body
All are exposed in the Zig module as window
, head
, body
constants to help
avoiding magic numbers in userland code.
Single DOM elements and entire element trees can be created via the
createElement()
function:
const dom = @import("wasmdom");
const handle = dom.createElement(&.{
// element tag
.tag = "div",
// CSS classes
.class = "bg-red white",
// parent element ID (here `document.body`)
.parent = dom.body,
// optional child element specs
.children = &.{
.{
.tag = "h1",
// text content for this element
.text = "OMG, it works!",
// nested childen
.children = &.{
.{
.tag = "span",
.text = "(recursive DOM creation FTW!)",
.class = "bg-yellow black",
},
},
},
},
});
The CreateElementOpts struct has some additional options and more are planned. All WIP!
Attributes can be provided as part of the CreateElementOpts
and/or accessed imperatively:
// creating & configuring an <input type="range"> element
_ = dom.createElement(&.{
.tag = "input",
.parent = toolbar,
.attribs = &.{
dom.Attrib.string("type", "range"),
dom.Attrib.number("min", 0),
dom.Attrib.number("max", 100),
dom.Attrib.number("step", 10),
dom.Attrib.number("value", 20),
},
});
The following accessors are provided (see /zig/lib.zig for documentation):
getStringAttrib()
/ setStringAttrib()
getNumericAttrib()
/ setNumericAttrib()
getBooleanAttrib()
/ setBooleanAttrib()
Once a DOM element has been created, event listeners can be attached to it. All
listeners take two arguments: an Event
struct and an optional opaque pointer
for passing arbitrary user context.
A more advanced version of the following click counter button component (written in Zig) can be seen in action in the zig-counter example project.
const wasm = @import("wasmapi");
const dom = @import("wasmdom");
/// Simple click counter component
const Counter = struct {
listener: dom.EventListener,
elementID: i32,
listenerID: u16,
clicks: usize,
step: usize,
const Self = @This();
/// Initialize internal state & DOM element w/ listener
pub fn init(self: *Self, parent: i32, step: usize) !void {
self.clicks = 0;
self.step = step;
// create DOM button element
self.elementID = dom.createElement(&.{
.tag = "button",
.class = "db w5 ma2 tc",
.text = "click me!",
.parent = parent,
});
// define & add click event listener w/ user context arg
self.listener = .{ .callback = onClick, .ctx = self };
self.listenerID = try dom.addListener(self.elementID, "click", &self.listener);
}
fn update(self: *const Self) void {
// format new button label
var buf: [32]u8 = undefined;
var label = std.fmt.bufPrint(&buf, "clicks: {d:0>4}", .{self.clicks}) catch return;
// update DOM element
dom.setInnerText(self.elementID, label);
}
/// event listener & state update
fn onClick(_: *const dom.Event, raw: ?*anyopaque) void {
// safely cast raw pointer
if (wasm.ptrCast(*Self, raw)) |self| {
self.clicks += self.step;
self.update();
}
}
};
ALPHA - bleeding edge / work-in-progress
Search or submit any issues for this package
yarn add @thi.ng/wasm-api-dom
ES module import:
<script type="module" src="https://cdn.skypack.dev/@thi.ng/wasm-api-dom"></script>
For Node.js REPL:
# with flag only for < v16
node --experimental-repl-await
> const wasmApiDom = await import("@thi.ng/wasm-api-dom");
Package sizes (gzipped, pre-treeshake): ESM: 4.19 KB
Several demos in this repo's /examples directory are using this package.
A selection:
Screenshot | Description | Live demo | Source |
---|---|---|---|
Zig-based DOM creation & canvas drawing app | Demo | Source | |
Simple Zig/WASM click counter DOM component | Demo | Source |
For now, please see the package docs, source code comments (TS & Zig) and the various comments in the zig-canvas example project for further reference and usage patterns! Thank you!
If this project contributes to an academic publication, please cite it as:
@misc{thing-wasm-api-dom,
title = "@thi.ng/wasm-api-dom",
author = "Karsten Schmidt and others",
note = "https://thi.ng/wasm-api-dom",
year = 2022
}
© 2022 Karsten Schmidt // Apache Software License 2.0
FAQs
Browser DOM bridge API for hybrid TypeScript & WASM (Zig) applications
The npm package @thi.ng/wasm-api-dom receives a total of 88 weekly downloads. As such, @thi.ng/wasm-api-dom popularity was classified as not popular.
We found that @thi.ng/wasm-api-dom demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.