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

@extism/extism

v2.0.0-rc10

Published

Extism runtime for JavaScript

Downloads

699

Readme

Extism JS SDK

This is a universal JavaScript SDK for Extism. It works in all the major JavaScript runtimes:

  • Browsers (Firefox, Chrome, WebKit)
  • Node
  • Deno
  • Bun
  • Cloudflare Workers
  • interested in others? Let us know!

Instead of using FFI and the libextism shared object, this library uses whatever Wasm runtime is already available with the JavaScript runtime.

Installation

Install via npm:

$ npm install @extism/extism

Note: Keep in mind we will possibly have breaking changes b/w rc versions until we hit 1.0.

Compatibility

  • Node.js: v18+ (with --experimental-global-webcrypto); v20 with no additional flags
  • Deno: v1.36+
  • Bun: Tested on v1.0.7; Bun partially implements WASI.

Browser tests are run using playwright's defaults. In browsers, background thread support requires SharedArrayBuffer and Atomic support. This is only available in crossOriginIsolated contexts.

Reference Docs

Reference docs can be found at https://extism.github.io/js-sdk/.

Getting Started

This guide should walk you through some of the concepts in Extism and this JS library.

First you should import createPlugin from Extism:

// CommonJS
const createPlugin = require("@extism/extism")

// ES Modules/Typescript
import createPlugin from '@extism/extism';

// Deno
import createPlugin from "jsr:@extism/extism";

Creating A Plug-in

The primary concept in Extism is the plug-in. You can think of a plug-in as a code module stored in a .wasm file.

Plug-in code can come from a file on disk, object storage or any number of places. Since you may not have one handy let's load a demo plug-in from the web:

const plugin = await createPlugin(
    'https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm',
    { useWasi: true }
);

Note: Plug-ins can be loaded in a variety of ways. See the reference docs for createPlugin and read about the manifest.

Calling A Plug-in's Exports

We're using a plug-in, count_vowels, which was compiled from Rust. count_vowels plug-in does one thing: it counts vowels in a string. As such, it exposes one "export" function: count_vowels. We can call exports using Plugin.call:

let out = await plugin.call("count_vowels", input);
console.log(out.text())

// => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}

All plug-in exports have a simple interface of optional bytes in, and optional bytes out. This plug-in happens to take a string and return a JSON encoded string with a report of results.

Plug-in State

Plug-ins may be stateful or stateless. Plug-ins can maintain state between calls by the use of variables. Our count_vowels plug-in remembers the total number of vowels it's ever counted in the total key in the result. You can see this by making subsequent calls to the export:

let out = await plugin.call("count_vowels", "Hello, World!");
console.log(out.text())
// => {"count": 3, "total": 9, "vowels": "aeiouAEIOU"}

out = await plugin.call("count_vowels", "Hello, World!");
console.log(out.json())
// => {"count": 3, "total": 9, "vowels": "aeiouAEIOU"}

These variables will persist until you call await plugin.reset(). Variables are not shared between plugin instances.

Configuration

Plug-ins may optionally take a configuration object. This is a static way to configure the plug-in. Our count-vowels plugin takes an optional configuration to change out which characters are considered vowels. Example:

const wasm = {
    url: 'https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm'
}

let plugin = await createPlugin(wasm, {
    useWasi: true,
});

let out = await plugin.call("count_vowels", "Yellow, World!");
console.log(out.text())
// => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}

plugin = await createPlugin(wasm, {
    useWasi: true,
    config: { "vowels": "aeiouyAEIOUY" }
});

out = await plugin.call("count_vowels", "Yellow, World!");
console.log(out.text())
// => {"count": 4, "total": 4, "vowels": "aeiouAEIOUY"}

Host Functions

Let's extend our count-vowels example a little bit: Instead of storing the total in an ephemeral plug-in var, let's store it in a persistent key-value store!

Wasm can't use our KV store on it's own. This is where Host Functions come in.

Host functions allow us to grant new capabilities to our plug-ins from our application. They are simply some JS functions you write which can be passed down and invoked from any language inside the plug-in.

Let's load the manifest like usual but load up this count_vowels_kvstore plug-in:

const wasm = {
    url: "https://github.com/extism/plugins/releases/latest/download/count_vowels_kvstore.wasm"
}

Note: The source code for this is here and is written in Rust, but it could be written in any of our PDK languages.

Unlike our previous plug-in, this plug-in expects you to provide host functions that satisfy our its import interface for a KV store.

We want to expose two functions to our plugin, kv_write(key: string, value: Uint8Array) which writes a bytes value to a key and kv_read(key: string): Uint8Array which reads the bytes at the given key.

// pretend this is Redis or something :)
let kvStore = new Map();

const options = {
    useWasi: true,
    functions: {
        "extism:host/user": {
            // NOTE: the first argument is always a CurrentPlugin
            kv_read(cp: CurrentPlugin, offs: bigint) {
                const key = cp.read(offs).text();
                let value = kvStore.get(key) ?? new Uint8Array([0, 0, 0, 0]);
                console.log(`Read ${new DataView(value.buffer).getUint32(0, true)} from key=${key}`);
                return cp.store(value);
            },
            kv_write(cp: CurrentPlugin, kOffs: bigint, vOffs: bigint) {
                const key = cp.read(kOffs).text();

                // Value is a PluginOutput, which subclasses DataView. Along
                // with the `text()` and `json()` methods we've seen, we also
                // get DataView methods, such as `getUint32`.
                const value = cp.read(vOffs);
                console.log(`Writing value=${new value.getUint32(0, true)} from key=${key}`);

                kvStore.set(key, value.bytes());
            }
        }
    }
};

Note: In order to write host functions you should get familiar with the methods on the CurrentPlugin type.

We need to pass these imports to the plug-in to create them. All imports of a plug-in must be satisfied for it to be initialized:

const plugin = await createPlugin(wasm, options);

Now we can invoke the event:

let out = await plugin.call("count_vowels", "Hello World!");
console.log(out.text())
// => Read from key=count-vowels"
// => Writing value=3 from key=count-vowels"
// => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}

out = await plugin.call("count_vowels", "Hello World!");
console.log(out.text())
// => Read from key=count-vowels"
// => Writing value=6 from key=count-vowels"
// => {"count": 3, "total": 6, "vowels": "aeiouAEIOU"}

Run Examples:

npm run build

node --experimental-wasi-unstable-preview1 ./examples/node.js wasm/config.wasm

deno run -A ./examples/deno.ts ./wasm/config.wasm

bun run ./examples/node.js wasm/config.wasm