{"id":13603051,"url":"https://github.com/extism/js-sdk","last_synced_at":"2025-04-05T16:04:22.997Z","repository":{"id":196669026,"uuid":"674418865","full_name":"extism/js-sdk","owner":"extism","description":"Run Extism WebAssembly plug-ins / functions from JavaScript (supports major browsers, Node.js, Deno, Bun, and Cloudflare Workers)","archived":false,"fork":false,"pushed_at":"2025-02-27T17:28:25.000Z","size":6440,"stargazers_count":108,"open_issues_count":8,"forks_count":12,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-03-29T11:05:30.052Z","etag":null,"topics":["browser","bun","cloudflare","deno","extism","javascript","nodejs","wasm","web"],"latest_commit_sha":null,"homepage":"https://extism.github.io/js-sdk/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/extism.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-03T22:55:17.000Z","updated_at":"2025-03-27T14:50:18.000Z","dependencies_parsed_at":null,"dependency_job_id":"b0bd1bd7-774d-48ca-85cd-2c2b156ccfaf","html_url":"https://github.com/extism/js-sdk","commit_stats":null,"previous_names":["extism/js-sdk"],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fjs-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fjs-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fjs-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fjs-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/extism","download_url":"https://codeload.github.com/extism/js-sdk/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247361615,"owners_count":20926642,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["browser","bun","cloudflare","deno","extism","javascript","nodejs","wasm","web"],"created_at":"2024-08-01T18:01:47.709Z","updated_at":"2025-04-05T16:04:22.967Z","avatar_url":"https://github.com/extism.png","language":"TypeScript","readme":"# Extism JS SDK\n\nThis is a universal JavaScript SDK for Extism. It works in all the major JavaScript runtimes:\n\n* Browsers (Firefox, Chrome, WebKit)\n* Node\n* Deno\n* Bun\n* Cloudflare Workers\n* _interested in others? [Let us know!](https://github.com/extism/js-sdk/issues)_\n\nInstead of using FFI and the libextism shared object, this library uses whatever Wasm runtime is already available with the JavaScript runtime.\n\n## Installation\n\nInstall via npm:\n\n```shell\n$ npm install @extism/extism\n```\n\n\u003e **Note**: Keep in mind we will possibly have breaking changes b/w rc versions until we hit 1.0.\n\n## Compatibility\n\n- **Node.js**: `v18+` (with `--experimental-global-webcrypto`); `v20` with no additional flags\n- **Deno**: `v1.36+`\n- **Bun**: Tested on `v1.0.7`; Bun partially implements WASI.\n\nBrowser tests are run using [playwright](https://playwright.dev)'s defaults. In\nbrowsers, background thread support requires `SharedArrayBuffer` and `Atomic`\nsupport. This is only available in\n[`crossOriginIsolated`](https://developer.mozilla.org/en-US/docs/Web/API/crossOriginIsolated)\ncontexts.\n\n## Reference Docs\n\nReference docs can be found at [https://extism.github.io/js-sdk/](https://extism.github.io/js-sdk/).\n\n## Getting Started\n\nThis guide should walk you through some of the concepts in Extism and this JS library.\n\nFirst you should import `createPlugin` from Extism:\n```js\n// CommonJS\nconst createPlugin = require(\"@extism/extism\")\n\n// ES Modules/Typescript\nimport createPlugin from '@extism/extism';\n\n// Deno\nimport createPlugin from \"jsr:@extism/extism\";\n```\n\n## Creating A Plug-in\n\nThe primary concept in Extism is the [plug-in](https://extism.org/docs/concepts/plug-in). You can think of a plug-in as a code module stored in a `.wasm` file.\n\nPlug-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:\n\n```js\nconst plugin = await createPlugin(\n    'https://cdn.modsurfer.dylibso.com/api/v1/module/be716369b7332148771e3cd6376d688dfe7ee7dd503cbc43d2550d76cb45a01d.wasm',\n    { useWasi: true }\n);\n```\n\n\u003e *Note*: Plug-ins can be loaded in a variety of ways. See the reference docs for [createPlugin](https://extism.github.io/js-sdk/functions/createPlugin.html)\n\u003e and read about the [manifest](https://extism.org/docs/concepts/manifest/).\n\n## Calling A Plug-in's Exports\n\nWe're using a plug-in, `count_vowels`, which was compiled from Rust.\n`count_vowels` plug-in does one thing: it counts vowels in a string. As such,\nit exposes one \"export\" function: `count_vowels`. We can call exports using\n`Plugin.call`:\n\n```js\nconst input = \"Hello World\";\nlet out = await plugin.call(\"count_vowels\", input);\nconsole.log(out.text());\n\n// =\u003e {\"count\": 3, \"total\": 3, \"vowels\": \"aeiouAEIOU\"}\n```\n\nAll plug-in exports have a simple interface of optional bytes in, and optional\nbytes out. This plug-in happens to take a string and return a JSON encoded\nstring with a report of results.\n\n### Plug-in State\n\nPlug-ins may be stateful or stateless. Plug-ins can maintain state between calls by\nthe use of variables. Our `count_vowels` plug-in remembers the total number of\nvowels it's ever counted in the `total` key in the result. You can see this by\nmaking subsequent calls to the export:\n\n```js\nlet out = await plugin.call(\"count_vowels\", \"Hello, World!\");\nconsole.log(out.text());\n// =\u003e {\"count\": 3, \"total\": 3, \"vowels\": \"aeiouAEIOU\"}\n\nout = await plugin.call(\"count_vowels\", \"Hello, World!\");\nconsole.log(out.json());\n// =\u003e {\"count\": 3, \"total\": 6, \"vowels\": \"aeiouAEIOU\"}\n```\n\nThese variables will persist until you call `await plugin.reset()`. Variables\nare not shared between plugin instances.\n\n### Configuration\n\nPlug-ins may optionally take a configuration object. This is a static way to\nconfigure the plug-in. Our count-vowels plugin takes an optional configuration\nto change out which characters are considered vowels. Example:\n\n```js\nconst wasm = {\n    url: 'https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm'\n}\n\nlet plugin = await createPlugin(wasm.url, {\n    useWasi: true,\n});\n\nlet out = await plugin.call(\"count_vowels\", \"Yellow, World!\");\nconsole.log(out.text());\n// =\u003e {\"count\": 3, \"total\": 3, \"vowels\": \"aeiouAEIOU\"}\n\nplugin = await createPlugin(wasm.url, {\n    useWasi: true,\n    config: { \"vowels\": \"aeiouyAEIOUY\" }\n});\n\nout = await plugin.call(\"count_vowels\", \"Yellow, World!\");\nconsole.log(out.text());\n// =\u003e {\"count\": 4, \"total\": 4, \"vowels\": \"aeiouAEIOUY\"}\n```\n\n### Host Functions\n\nLet's extend our count-vowels example a little bit: Instead of storing the\n`total` in an ephemeral plug-in var, let's store it in a persistent key-value\nstore!\n\nWasm can't use our KV store on its own. This is where [Host\nFunctions](https://extism.org/docs/concepts/host-functions) come in.\n\n[Host functions](https://extism.org/docs/concepts/host-functions) allow us to\ngrant new capabilities to our plug-ins from our application. They are simply\nsome JS functions you write which can be passed down and invoked from any\nlanguage inside the plug-in.\n\nLet's load the manifest like usual but load up this `count_vowels_kvstore`\nplug-in:\n\n```js\nconst wasm = {\n    url: \"https://github.com/extism/plugins/releases/latest/download/count_vowels_kvstore.wasm\"\n}\n```\n\n\u003e *Note*: The source code for this is [here](https://github.com/extism/plugins/blob/main/count_vowels_kvstore/src/lib.rs) and is written in Rust, but it could be written in any of our PDK languages.\n\nUnlike our previous plug-in, this plug-in expects you to provide host functions that satisfy its import interface for a KV store.\n\nWe 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`.\n```js\n// pretend this is Redis or something :)\nlet kvStore = new Map();\n\nconst options = {\n    useWasi: true,\n    functions: {\n        \"extism:host/user\": {\n            // NOTE: the first argument is always a CurrentPlugin\n            kv_read(cp: CurrentPlugin, offs: bigint) {\n                const key = cp.read(offs).text();\n                let value = kvStore.get(key) ?? new Uint8Array([0, 0, 0, 0]);\n                console.log(`Read ${new DataView(value.buffer).getUint32(0, true)} from key=${key}`);\n                return cp.store(value);\n            },\n            kv_write(cp: CurrentPlugin, kOffs: bigint, vOffs: bigint) {\n                const key = cp.read(kOffs).text();\n\n                // Value is a PluginOutput, which subclasses DataView. Along\n                // with the `text()` and `json()` methods we've seen, we also\n                // get DataView methods, such as `getUint32`.\n                const value = cp.read(vOffs);\n                console.log(`Writing value=${value.getUint32(0, true)} from key=${key}`);\n\n                kvStore.set(key, value.bytes());\n            }\n        }\n    }\n};\n```\n\n\u003e *Note*: In order to write host functions you should get familiar with the\n\u003e methods on the `CurrentPlugin` type.\n\nWe need to pass these imports to the plug-in to create them. All imports of a\nplug-in must be satisfied for it to be initialized:\n\n```js\nconst plugin = await createPlugin(wasm.url, options);\n```\n\nNow we can invoke the event:\n\n```js\nlet out = await plugin.call(\"count_vowels\", \"Hello World!\");\nconsole.log(out.text());\n// =\u003e Read from key=count-vowels\"\n// =\u003e Writing value=3 from key=count-vowels\"\n// =\u003e {\"count\": 3, \"total\": 3, \"vowels\": \"aeiouAEIOU\"}\n\nout = await plugin.call(\"count_vowels\", \"Hello World!\");\nconsole.log(out.text());\n// =\u003e Read from key=count-vowels\"\n// =\u003e Writing value=6 from key=count-vowels\"\n// =\u003e {\"count\": 3, \"total\": 6, \"vowels\": \"aeiouAEIOU\"}\n```\n\n## Run Examples:\n\n```\nnpm run build\n\nnode --experimental-wasi-unstable-preview1 ./examples/node.js wasm/config.wasm\n\ndeno run -A ./examples/deno.ts ./wasm/config.wasm\n\nbun run ./examples/node.js wasm/config.wasm\n```\n","funding_links":[],"categories":["TypeScript","browser"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextism%2Fjs-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fextism%2Fjs-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextism%2Fjs-sdk/lists"}