{"id":20388191,"url":"https://github.com/deislabs/wasm-linker-js","last_synced_at":"2025-04-12T10:36:20.735Z","repository":{"id":49535824,"uuid":"312081613","full_name":"deislabs/wasm-linker-js","owner":"deislabs","description":"A simple WebAssembly Linker in JavaScript","archived":false,"fork":false,"pushed_at":"2021-06-15T16:35:47.000Z","size":375,"stargazers_count":17,"open_issues_count":3,"forks_count":2,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-04-11T19:21:34.592Z","etag":null,"topics":["wasm","webassembly"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deislabs.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":null,"security":null,"support":null}},"created_at":"2020-11-11T20:25:54.000Z","updated_at":"2024-02-25T18:36:16.000Z","dependencies_parsed_at":"2022-08-20T17:10:57.424Z","dependency_job_id":null,"html_url":"https://github.com/deislabs/wasm-linker-js","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deislabs%2Fwasm-linker-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deislabs%2Fwasm-linker-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deislabs%2Fwasm-linker-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deislabs%2Fwasm-linker-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deislabs","download_url":"https://codeload.github.com/deislabs/wasm-linker-js/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248553993,"owners_count":21123562,"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":["wasm","webassembly"],"created_at":"2024-11-15T03:07:50.038Z","updated_at":"2025-04-12T10:36:20.549Z","avatar_url":"https://github.com/deislabs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wasm-linker-js\n\n### A simple WebAssembly Linker in JavaScript\n\n![actions badge][actions-badge] [![NPM version][npm-image]][npm]\n\nThis is _an experimental_ JavaScript library that helps instantiating\nWebAssembly modules with imports by providing functionality to link JavaScript\nobjects (functions, memories, globals) as imports, as well as automatically\nperform name based resolution for linking entire modules.\n\nThe API loosely follows the [Wasmtime][wasmtime] linker, (see the [linker\ndocumentation][wasmtime-linker]), and it exposes asynchronous import\nfunctionality enabled by [Binaryen][binaryen] and [Asyncify][asyncify].\n\n### Using the Linker\n\n\u003e For more examples of using the Linker in both TypeScript and JavaScript, check\n\u003e the [linker tests][linker-tests] and the [Node.js examples][node-examples].\n\nFirst, add the package to your project:\n\n```plaintext\n$ npm install @deislabs/wasm-linker-js\n```\n\n\u003e Note that in order to run the examples shown here, `binaryen` is also required\n\u003e (`npm install binaryen`), in order to show the text format of the WebAssembly\n\u003e modules. In real world scenarios that is not necessary, and the modules can be\n\u003e compiled from their binary representation without additional dependencies.\n\n#### Defining a single import\n\nAssuming we are trying to instantiate the module represented in its text format\nbelow (transformed to its binary representation using [Binaryen][binaryen]), we\ncan satisfy its import using the `define` method available on the linker:\n\n```js\nconst { Linker } = require(\"@deislabs/wasm-linker-js\");\nconst { parseText } = require(\"binaryen\");\n\nconst usingAdd = `\n(module\n    (import \"calculator\" \"add\" (func $calc_add (param i32 i32) (result i32)))\n  \n    (memory 1 1)\n    (export \"memory\" (memory 0))\n    (export \"add\" (func $add))\n\n    (func $add (param i32) (param i32) (result i32)\n        (return\n            (call $calc_add\n                (local.get 0)\n                (local.get 1)\n            )\n        )\n    )\n)\n`;\n\n(async () =\u003e {\n  var linker = new Linker();\n\n  // The \"usingAdd\" module imports calculator.add.\n  // We define it,  provide a JS implementation, then\n  // instantiate it.\n  linker.define(\"calculator\", \"add\", (a, b) =\u003e a + b);\n  var calc = await linker.instantiate(parseText(usingAdd).emitBinary());\n\n  var result = calc.instance.exports.add(1, 2);\n  console.log(result);\n})();\n```\n\n#### Linking an entire module\n\nIf we have a compiled module that exports items (defined below in its text\nformat and contained in the `add` constant) that our initial module needs to\nimport, we can add it to the linker, then continue instantiating our module\n(defined above in its text format and contained in the `usingAdd` constant):\n\n```js\nconst { Linker } = require(\"@deislabs/wasm-linker-js\");\nconst { parseText } = require(\"binaryen\");\n\nconst add = `\n(module\n  (memory 1 1)\n  (export \"memory\" (memory 0))\n  (export \"add\" (func $add))\n  \n  (func $add (param i32) (param i32) (result i32)\n      (return\n          (i32.add\n              (local.get 0)\n              (local.get 1)\n          )\n      )\n  )\n)\n`;\n\n(async () =\u003e {\n  var linker = new Linker();\n\n  // The \"usingAdd\" module above imports calculator.add.\n  // We link a module that exports the functionality\n  // required, then instantiate the module that uses it.\n  await linker.module(\n    \"calculator\",\n    new WebAssembly.Module(parseText(add).emitBinary())\n  );\n  var calc = await linker.instantiate(parseText(usingAdd).emitBinary());\n  var result = calc.instance.exports.add(1, 2);\n  console.log(result);\n})();\n```\n\n#### Defining asynchronous imports\n\nThe current WebAssembly MVP does not have a way of waiting for the execution of\nasynchronous imports (see [this issue][async-wasm-issue]). To enable this\nfunctionality, [Binaryen][binaryen] has a pass that [transforms a Wasm module\nand allows it to pause and resume by unwiding and rewinding the call\nstack][asyncify-blog]. When enabled, this library can use the [JavaScript\nwrapper of Asyncify][asyncify] and define asynchronous import functions for\nWebAssembly modules (note that the Asyncify pass must have been applied to the\nmodule before instantiating using the linker):\n\n```js\nconst { Linker } = require(\"@deislabs/wasm-linker-js\");\nconst { parseText } = require(\"binaryen\");\n\n(async () =\u003e {\n  var useAsyncify = true;\n  var linker = new Linker(useAsyncify);\n\n  // Notice how we define an asynchronous import, which\n  // will wait for 1.5s before returning the result.\n  var sleep = function (ms) {\n    return new Promise((resolve) =\u003e {\n      setTimeout(resolve, ms);\n    });\n  };\n  linker.define(\"calculator\", \"add\", async (a, b) =\u003e {\n    await sleep(1500);\n    return a + b;\n  });\n\n  let bytes = parseText(usingAdd);\n\n  // we perform the asyncify compiler pass from Binaryen\n  bytes.runPasses([\"asyncify\"]);\n  var calc = await linker.instantiate(bytes.emitBinary());\n\n  var result = await calc.instance.exports.add(1, 2);\n  console.log(result);\n})();\n```\n\n#### Using the streaming APIs in the browser\n\nFor browsers that support the WebAssembly streaming APIs, the linker exposes two\nmethods that can be used to efficiently instantiate modules from a streamed\nsource: `moduleStreaming`, which instantiates a module and adds its exports to\nthe linker's cache, and `instantiateStreaming`, which instantiates a module and\nreturns its the `WebAssemblyInstantiatedSource`:\n\n```js\n(async () =\u003e {\n  var linker = new Linker();\n  await linker.moduleStreaming(\"calculator\", fetch(\"calculator.wasm\"));\n  var mod = await linker.instantiateStreaming(fetch(\"using_calculator.wasm\"));\n  console.log(mod.instance.exports.multiply(3, 4));\n}\n```\n\nSee [the documentation for the browser streaming APIs][mdn-streaming] for more\ninformation about instantiating modules from streamed sources.\n\nThe linker also allows adding an already instantiated module, through the\n`instance` method, and aliasing a module under a new name, through the `alias`\nmethod. Most public methods defined on the Linker have a correspondent in the\n[Wasmtime][wasmtime] Linker, and we try to keep the APIs similar.\n\n### Implementation notes and known issues\n\n- When defining multiple import items with the same name, the last one takes\n  precedence (the existing items are replaced). This behavior could change in\n  the future to add a configurable property defining whether import shadowing\n  should be allowed.\n- When instantiating a linker with Asyncify enabled, _all_ modules linked and\n  instantiated with the linker will be instantiated using Asyncify's JavaScript\n  wrapper. This behavior could change in the future to allow a per-instance (and\n  by extension per module linked) setting for Asyncify. (this can be avoided\n  through instantiating a module separately and adding it to the linker using\n  the `instance` method).\n- There is a [browser example in the `examples/` directory][browser-demo] in\n  this repository. While functional, the implementation is far from ideal - the\n  WebPack configuration for generating a browser-compatible library is not\n  optimal (this should be changed to use ECMAScript modules).\n- **This library is experimental, and the API is not stable**. We welcome\n  feedback on both the public API and the implementation of this library.\n\n### Contributing\n\nThis project welcomes contributions through the GitHub pull request process.\nPrerequisites to building the project:\n\n- Node.js\n- `npm`\n\nTo iterate on the project locally:\n\n```plaintext\n$ npm run build\n$ npm test\n$ npm run examples\n```\n\n### Code of Conduct\n\nThis project has adopted the\n[Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\n\nFor more information see the\n[Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any\nadditional questions or comments.\n\n[wasmtime]: https://github.com/bytecodealliance/wasmtime\n[wasmtime-linker]: https://docs.rs/wasmtime/0.21.0/wasmtime/\n[binaryen]: https://github.com/WebAssembly/binaryen\n[asyncify]: https://github.com/GoogleChromeLabs/asyncify\n[async-wasm-issue]: https://github.com/WebAssembly/design/issues/720\n[asyncify-blog]: https://kripken.github.io/blog/wasm/2019/07/16/asyncify.html\n[browser-demo]: examples/index.html\n[node-examples]: examples/node-example.js\n[linker-tests]: tests/linker.ts\n[mdn-streaming]:\n  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiateStreaming\n[npm-image]: https://badge.fury.io/js/%40deislabs%2Fwasm-linker-js.svg\n[npm]: https://www.npmjs.com/package/@deislabs/wasm-linker-js\n[actions-badge]:\n  https://github.com/deislabs/wasm-linker-js/workflows/Build%20and%20Test/badge.svg\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeislabs%2Fwasm-linker-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeislabs%2Fwasm-linker-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeislabs%2Fwasm-linker-js/lists"}