npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@bytecodealliance/componentize-js

v0.14.0

Published

ESM -> WebAssembly Component creator, via a SpiderMonkey JS engine embedding

Downloads

57,583

Readme

A Bytecode Alliance project

Overview

Provides a Mozilla SpiderMonkey embedding that takes as input a JavaScript source file and a WebAssembly Component WIT World, and outputs a WebAssembly Component binary with the same interface.

Note: This is an experimental project, no guarantees are provided for stability or support and breaking changes may be made in future.

Example

See the end-to-end example workflow for creating a JS component and running it in Wasmtime or Node.js.

Explainer

For background on the concepts involved, see https://bytecodealliance.org/articles/making-javascript-run-fast-on-webassembly.

The goal of this project specifically is to provide a comprehensive dynamic bindings system for creating arbitrary WebAssembly Components from JavaScript. That is, to provide full flexibility over the resultant JS environment and WIT World.

Wizer Pre-Initialization

Adaption follows the standard Wizer technique in pre-initializing a snapshot of the runtime against the source and bindings.

The snapshotting process executes the JS engine initialization, globals and parsing and compiling of the source code. Currently we also evaluate the top-level of the source so that the executed exports of the top-level ES module are provided already initialized.

As a result, at runtime - only the bytecode is being executed, without any initialization costs. This makes on-demand Wasm execution of JS incredibly fast.

SpiderMonkey Embedding

As a dynamic language with quirks, JavaScript cannot be compiled directly into bytecode without including a comprehensive ECMA-262 spec-compliant runtime engine. Componentization of JavaScript thus involves embedding the JS runtime engine into the component itself.

SpiderMonkey is chosen here as a JS engine with first-class WASI build support, using an embedding of the StarlingMonkey Wasm engine. The total embedding size is around 8MB.

One of the security benefits of the component model is complete code isolation apart from the shared-nothing code boundaries between components. By fully encapsulating the engine embedding for each individual component, this maintains comprehensive per-component isolation.

As more components are written in JavaScript, and there exist scenarios where multiple JS components are communicating in the same application, the plan for optimization here is to share the SpiderMonkey engine embedding between them. This can be done without breaking the shared-nothing semantics by having the engine itself loaded as a shared library of the components. Sharing functions via same SpiderMonkey build, not memory.

Establishing this initial prototype as a singular flexible engine foundation that can be turned into a shared library is therefore the focus for this project.

Weval AOT Compilation

Platform APIs

The following APIs are available:

  • Legacy Encoding: atob, btoa, decodeURI, encodeURI, decodeURIComponent, encodeURIComponent
  • Streams: ReadableStream, ReadableStreamBYOBReader, ReadableStreamBYOBRequest, ReadableStreamDefaultReader, ReadableStreamDefaultController, ReadableByteStreamController, WritableStream ByteLengthQueuingStrategy CountQueuingStrategy, TransformStream
  • URL: URL URLSearchParams
  • Console: console
  • Performance: Performance
  • Task: queueMicrotask, setInterval setTimeout clearInterval clearTimeout
  • Location: WorkerLocation, location
  • Encoding: TextEncoder, TextDecoder, CompressionStream, DecompressionStream
  • Structured Clone: structuredClone
  • Fetch: fetch Request Response Headers
  • Crypto: SubtleCrypto Crypto crypto CryptoKey

Usage

Install and run as a Node.js library:

npm install @bytecodealliance/componentize-js
import { componentize } from '@bytecodealliance/componentize-js';
import { writeFile } from 'node:fs/promises';

const { component } = await componentize(`
  import { log } from 'local:hello/logger';

  export function sayHello (name) {
    log(`Hello ${name}`);
  }

`, `
  package local:hello;
  interface logger {
    log: func(msg: string);
  }
  world hello {
    import logger; 
    export say-hello: func(name: string);
  }
`);

await writeFile('test.component.wasm', component);

See types.d.ts for the full interface options.

The component iself can be executed in any component runtime, see the example for an end to end workflow in Wasmtime.

AOT Compilation

To enable AOT compilation, set the enableAot: true option to run Weval ahead-of-time compilation.

Async Support

To support asynchronous operations, all functions may optionally be written as sync or async functions, even though they will always be turned into sync component functions.

For example, to use fetch which requires async calls, we can write the same example component using an async function:

export async function sayHello (name) {
  const text = await (await fetch(`http://localhost:8080/${name}`)).text();
  console.log(text);
}

ComponentizeJS will automatically resolve promises returned by functions to syncify their return values, running the event loop within the JS component to resolution.

This asynchrony is only supported for exported functions - imported functions can only be synchronous pending component-model-level async support.

CLI

ComponentizeJS can be used as a CLI from jco:

npm install -g @bytecodealliance/jco @bytecodealliance/componentize-js

For example:

jco componentize source.js --wit wit -o component.wasm

See jco componentize --help for more details.

Features

The set of enabled features in the engine can be customized depending on the target world and expected capabilities.

The default set of features includes:

  • 'stdio': Output to stderr and stdout for errors and console logging, depends on wasi:cli and wasi:io.
  • 'random': Support for cryptographic random, depends on wasi:random. When disabled, random numbers will still be generated but will not be random and instead fully deterministic.
  • 'clocks': Support for clocks and duration polls, depends on wasi:clocks and wasi:io. When disabled, using any timer functions like setTimeout or setInterval will panic.
  • 'http': Support for outbound HTTP via the fetch global in JS.

Setting disableFeatures: ['random', 'stdio', 'clocks', 'http'] will disable all features creating a minimal "pure component", that does not depend on any WASI APIs at all and just the target world.

Note that pure components will not report errors and will instead trap, so that this should only be enabled after very careful testing.

Note that features explicitly imported by the target world cannot be disabled - if you target a component to a world that imports wasi:clocks, then disableFeatures: ['clocks'] will not be supported.

Using StarlingMonkey's fetch-event

The StarlingMonkey engine provides the ability to use fetchEvent to handle calls to wasi:http/[email protected]#handle. When targeting worlds that export wasi:http/[email protected] the fetch event will automatically be attached. Alternatively, to override the fetch event with a custom handler, export an explict incomingHandler or 'wasi:http/[email protected]' object. Using the fetchEvent requires enabling the http feature.

API

export function componentize(jsSource: string, opts: {
  witPath: string,
  worldName: string,
  debug?: bool,
  sourceName?: string,
  engine?: string,
  preview2Adapter?: string,
  disableFeatures?: ('stdio' | 'random' | 'clocks' | 'http')[],
}): {
  component: Uint8Array,
  imports: string[]
}

http provides support for the host APIs used by the fetch method and is disabled by default, while this API is still being developed. Contributions very welcome to improve fetch support.

Converts a JS source into a component binary.

Imports provides the list of used guest imports only, while the StarlingMonkey engine may pull in additional imports. Direct component analysis should be used to correctly infer the real imports list.

Contributing

Pre-requisites

  • git submodule update --init --recursive to update the submodules.
  • Stable Rust with the wasm32-unknown-unknown and wasm32-wasi targets installed.
  • wasi-sdk-20.0 installed at /opt/wasi-sdk/

Building and testing

Building and testing is based on a npm install && npm run build && npm run test workflow.

License

This project is licensed under the Apache 2.0 license with the LLVM exception. See LICENSE for more details.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.