Browse Source

pack wasm

thesimplekid 1 year ago
parent
commit
cbe16b5420

+ 3 - 0
bindings/cashu-js/Cargo.toml

@@ -20,6 +20,9 @@ serde_json.workspace = true
 serde.workspace = true
 wasm-bindgen = { version = "0.2.87", features = ["serde-serialize"] }
 wasm-bindgen-futures = "0.4.37"
+console_error_panic_hook = "0.1"
+
+
 
 [package.metadata.wasm-pack.profile.release]
 wasm-opt = true

+ 10 - 3
bindings/cashu-js/examples/amount.js

@@ -1,5 +1,12 @@
-const Amount = require("../");
+const {Amount, loadWasmAsync, loadWasmSync } = require("..");
 
-let amount = Amount.fromSat(10);
 
-console.log(amount)
+function main() {
+  loadWasmSync();
+
+  let amount = Amount.fromSat(BigInt(10));
+
+  console.log(amount.toSat())
+}
+
+main();

+ 1 - 1
bindings/cashu-js/justfile

@@ -2,4 +2,4 @@ build:
 	wasm-pack build
 
 pack:
-	wasm-pack pack
+	npm run package

+ 3 - 0
bindings/cashu-js/package.json

@@ -37,5 +37,8 @@
     "node": ">= 10"
   },
   "scripts": {
+    "build": "WASM_PACK_ARGS=--release ./scripts/build.sh",
+    "build:dev": "WASM_PACK_ARGS=--dev ./scripts/build.sh",
+    "package": "npm run build && npm pack"
   }
 }

+ 21 - 0
bindings/cashu-js/scripts/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022-2023 Yuki Kishimoto
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 35 - 0
bindings/cashu-js/scripts/build.sh

@@ -0,0 +1,35 @@
+#!/bin/bash
+#
+# Build the JavaScript modules
+#
+# This script is really a workaround for https://github.com/rustwasm/wasm-pack/issues/1074.
+#
+# Currently, the only reliable way to load WebAssembly in all the JS
+# environments we want to target (web-via-webpack, web-via-browserify, jest)
+# seems to be to pack the WASM into base64, and then unpack it and instantiate
+# it at runtime.
+#
+# Hopefully one day, https://github.com/rustwasm/wasm-pack/issues/1074 will be
+# fixed and this will be unnecessary.
+
+set -e
+
+cd $(dirname "$0")/..
+
+WASM_BINDGEN_WEAKREF=1 wasm-pack build --target nodejs --scope rust-cashu --out-dir pkg "${WASM_PACK_ARGS[@]}"
+
+# Convert the Wasm into a JS file that exports the base64'ed Wasm.
+echo "module.exports = \`$(base64 pkg/cashu_js_bg.wasm)\`;" > pkg/cashu_js_bg.wasm.js
+
+# In the JavaScript:
+#  1. Strip out the lines that load the WASM, and our new epilogue.
+#  2. Remove the imports of `TextDecoder` and `TextEncoder`. We rely on the global defaults.
+{
+  sed -e '/Text..coder.*= require(.util.)/d' \
+      -e '/^const path = /,$d' pkg/cashu_js.js
+  cat scripts/epilogue.js
+} > pkg/cashu_js.js.new
+mv pkg/cashu_js.js.new pkg/cashu_js.js
+
+# also extend the typescript
+cat scripts/epilogue.d.ts >> pkg/cashu_js.d.ts

+ 10 - 0
bindings/cashu-js/scripts/epilogue.d.ts

@@ -0,0 +1,10 @@
+/**
+ * Load the WebAssembly module in the background, if it has not already been loaded.
+ *
+ * Returns a promise which will resolve once the other methods are ready.
+ *
+ * @returns {Promise<void>}
+ */
+ export function loadWasmAsync(): Promise<void>;
+
+ export function loadWasmSync(): void;

+ 76 - 0
bindings/cashu-js/scripts/epilogue.js

@@ -0,0 +1,76 @@
+let inited = false;
+module.exports.loadWasmSync = function () {
+    if (inited) {
+        return;
+    }
+    if (initPromise) {
+        throw new Error("Asynchronous initialisation already in progress: cannot initialise synchronously");
+    }
+    const bytes = unbase64(require("./cashu_js_bg.wasm.js"));
+    const mod = new WebAssembly.Module(bytes);
+    const instance = new WebAssembly.Instance(mod, imports);
+    wasm = instance.exports;
+    wasm.__wbindgen_start();
+    inited = true;
+};
+
+let initPromise = null;
+
+/**
+ * Load the WebAssembly module in the background, if it has not already been loaded.
+ *
+ * Returns a promise which will resolve once the other methods are ready.
+ *
+ * @returns {Promise<void>}
+ */
+module.exports.loadWasmAsync = function () {
+    if (inited) {
+        return Promise.resolve();
+    }
+    if (!initPromise) {
+        initPromise = Promise.resolve()
+            .then(() => require("./cashu_js_bg.wasm.js"))
+            .then((b64) => WebAssembly.instantiate(unbase64(b64), imports))
+            .then((result) => {
+                wasm = result.instance.exports;
+                wasm.__wbindgen_start();
+                inited = true;
+            });
+    }
+    return initPromise;
+};
+
+const b64lookup = new Uint8Array([
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 62, 0, 62, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7,
+    8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32,
+    33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+]);
+
+// base64 decoder, based on the code at https://developer.mozilla.org/en-US/docs/Glossary/Base64#solution_2_%E2%80%93_rewriting_atob_and_btoa_using_typedarrays_and_utf-8
+function unbase64(sBase64) {
+    const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, "");
+    const nInLen = sB64Enc.length;
+    const nOutLen = (nInLen * 3 + 1) >> 2;
+    const taBytes = new Uint8Array(nOutLen);
+
+    let nMod3;
+    let nMod4;
+    let nUint24 = 0;
+    let nOutIdx = 0;
+    for (let nInIdx = 0; nInIdx < nInLen; nInIdx++) {
+        nMod4 = nInIdx & 3;
+        nUint24 |= b64lookup[sB64Enc.charCodeAt(nInIdx)] << (6 * (3 - nMod4));
+        if (nMod4 === 3 || nInLen - nInIdx === 1) {
+            nMod3 = 0;
+            while (nMod3 < 3 && nOutIdx < nOutLen) {
+                taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255;
+                nMod3++;
+                nOutIdx++;
+            }
+            nUint24 = 0;
+        }
+    }
+
+    return taBytes;
+}

+ 9 - 0
bindings/cashu-js/src/lib.rs

@@ -1,3 +1,12 @@
+use wasm_bindgen::prelude::*;
+
 pub mod error;
 pub mod nuts;
 pub mod types;
+
+pub use types::JsAmount;
+
+#[wasm_bindgen(start)]
+pub fn start() {
+    console_error_panic_hook::set_once();
+}

+ 2 - 2
bindings/cashu-js/src/types/amount.rs

@@ -26,9 +26,9 @@ impl From<Amount> for JsAmount {
 #[wasm_bindgen(js_class = Amount)]
 impl JsAmount {
     #[wasm_bindgen(constructor)]
-    pub fn new(sats: u64) -> Self {
+    pub fn new(sats: u32) -> Self {
         Self {
-            inner: Amount::from_sat(sats),
+            inner: Amount::from_sat(sats as u64),
         }
     }
 

+ 1 - 1
bindings/cashu-js/tsconfig.json

@@ -3,7 +3,7 @@
         "strict": true
     },
     "typedocOptions": {
-        "entryPoints": ["pkg/nostr_js.d.ts"],
+        "entryPoints": ["pkg/cashu_js.d.ts"],
         "readme": "README.md"
     }
 }