{"id":16048928,"url":"https://github.com/devongovett/napi-wasm","last_synced_at":"2025-04-12T18:49:15.081Z","repository":{"id":66053703,"uuid":"582904344","full_name":"devongovett/napi-wasm","owner":"devongovett","description":"An implementation of the napi API for WASM. ","archived":false,"fork":false,"pushed_at":"2024-08-31T20:35:56.000Z","size":62,"stargazers_count":203,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-03T20:13:16.540Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devongovett.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-28T07:31:31.000Z","updated_at":"2025-02-28T11:15:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"b199d54e-7892-44e8-85e5-9888a50d024a","html_url":"https://github.com/devongovett/napi-wasm","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devongovett%2Fnapi-wasm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devongovett%2Fnapi-wasm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devongovett%2Fnapi-wasm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devongovett%2Fnapi-wasm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devongovett","download_url":"https://codeload.github.com/devongovett/napi-wasm/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248618218,"owners_count":21134199,"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":[],"created_at":"2024-10-09T00:11:21.409Z","updated_at":"2025-04-12T18:49:15.062Z","avatar_url":"https://github.com/devongovett.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# napi-wasm\n\nAn implementation of the [napi](https://nodejs.org/api/n-api.html) API for WASM. Enables using some native Node modules in browsers and other environments.\n\n## Setup\n\nTo use napi-wasm, there are a few requirements:\n\n1. Configure your linker to export an indirect function table. With ldd, this is the `--export-table` flag. This enables JavaScript to call callback functions registered by WASM. It is exposed in the WebAssembly exports as `__indirect_function_table`.\n2. Export a function from your WASM build named `napi_register_module_v1` (Node's default), or `napi_register_wasm_v1` for WASM-specific builds. This is called during initialization to setup the `exports` object for your module. It receives an environment and an exports object pointer as arguments, which you can add properties to.\n3. Include a function named `napi_wasm_malloc` in your WASM build. This is called from JavaScript by napi-wasm to allocate memory in the WASM heap. It should accept a `uint32` size argument indicating the number of bytes to allocate, and return a `uint8` pointer to allocated memory.\n4. Compile for the `wasm32-unknown-unknown` target.\n\n### In Rust\n\nThe above steps should apply for any programming language, but here's an example in Rust. First, define a `napi_wasm_malloc` function so JavaScript can allocate memory in the WASM heap using the default allocator.\n\n```rust\nuse std::alloc::{alloc, Layout};\n\n#[no_mangle]\npub extern \"C\" fn napi_wasm_malloc(size: usize) -\u003e *mut u8 {\n  let align = std::mem::align_of::\u003cusize\u003e();\n  if let Ok(layout) = Layout::from_size_align(size, align) {\n    unsafe {\n      if layout.size() \u003e 0 {\n        let ptr = alloc(layout);\n        if !ptr.is_null() {\n          return ptr;\n        }\n      } else {\n        return align as *mut u8;\n      }\n    }\n  }\n\n  std::process::abort();\n}\n```\n\nNext, implement `napi_register_wasm_v1` to register your module exports. We'll use the [napi-rs](https://github.com/napi-rs/napi-rs) bindings in this example to make it a bit nicer than calling C APIs directly. Note that the napi-rs `#[module_exports]` macro currently doesn't work in WASM because Rust doesn't support ctor setup functions in WASM targets yet, so we'll need to do this manually.\n\n```rust\nuse napi::{Env, JsObject, NapiValue};\n\n#[no_mangle]\npub unsafe extern \"C\" fn napi_register_wasm_v1(raw_env: napi::sys::napi_env, raw_exports: napi::sys::napi_value) {\n  let env = Env::from_raw(raw_env);\n  let exports = JsObject::from_raw_unchecked(raw_env, raw_exports);\n\n  exports.create_named_method(\"transform\", transform);\n}\n\n#[js_function(1)]\nfn transform(ctx: CallContext) -\u003e napi::Result\u003cJsUnknown\u003e {\n  // ...\n}\n```\n\nTo compile, you need to export a function table and use the correct target.\n\n```shell\n RUSTFLAGS=\"-C link-arg=--export-table\" cargo build --target wasm32-unknown-unknown\n```\n\nThis will output a file in `target/wasm32-unknown-unknown/debug/YOUR_CRATE.wasm` which you can load in a JavaScript environment.\n\nYou can also put the rust flags in a `.cargo/config.toml` file so you don't need to provide the environment variable each time you run `cargo build`.\n\n```toml\n[target.wasm32-unknown-unknown]\nrustflags = [\"-C\", \"link-arg=--export-table\"]\n```\n\n### Loading\n\nTo load a WASM file and initialize a napi environment, you'll need to import the `napi-wasm` package. You instantiate a WASM module as usual, providing `napi` as the `env` import key. This provides the napi functions for your WASM module to use.\n\nThen, pass the WASM instance to the `Environment` constructor to setup a napi environment. This will call `napi_register_wasm_v1` or `napi_register_module_v1` to setup the exports object. Then you can call functions on the exports object as you would in Node.\n\n```js\nimport { Environment, napi } from 'napi-wasm';\n\n// Construct a URL and instantiate a WebAssembly module as usual.\nconst url = new URL('path/to/lib.wasm', import.meta.url);\nconst { instance } = await WebAssembly.instantiateStreaming(fetch(url), {\n  env: napi\n});\n\n// Create an environment.\nlet env = new Environment(instance);\nlet exports = env.exports;\n\n// Use exports as usual!\nexports.transform({\n  // ...\n});\n```\n\nWhen you are done with an `Environment`, call the `destroy()` function to clean up memory.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevongovett%2Fnapi-wasm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevongovett%2Fnapi-wasm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevongovett%2Fnapi-wasm/lists"}