Initial commit
commit
670b265bdd
@ -0,0 +1,5 @@
|
||||
target
|
||||
index.node
|
||||
**/node_modules
|
||||
**/.DS_Store
|
||||
npm-debug.log*
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "node-veilid"
|
||||
version = "0.1.0"
|
||||
description = "Node.js bindings for Veilid"
|
||||
authors = ["Sven Slootweg <admin@cryto.net>"]
|
||||
license = "WTFPL OR CC0-1.0"
|
||||
edition = "2021"
|
||||
exclude = ["index.node"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.30.0", features = ["macros", "rt-multi-thread"] }
|
||||
veilid-core = { git = "https://gitlab.com/veilid/veilid", tag="v0.2.3", features = [ "rt-tokio" ] }
|
||||
once_cell = "1"
|
||||
thiserror = "1.0.49"
|
||||
flume = "0.11.0"
|
||||
|
||||
[dependencies.neon]
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
features = ["napi-6", "promise-api", "channel-api"]
|
@ -0,0 +1,121 @@
|
||||
# node-veilid
|
||||
|
||||
**node-veilid:** Node.js bindings for Veilid
|
||||
|
||||
This project was bootstrapped by [create-neon](https://www.npmjs.com/package/create-neon).
|
||||
|
||||
## Installing node-veilid
|
||||
|
||||
Installing node-veilid requires a [supported version of Node and Rust](https://github.com/neon-bindings/neon#platform-support).
|
||||
|
||||
You can install the project with npm. In the project directory, run:
|
||||
|
||||
```sh
|
||||
$ npm install
|
||||
```
|
||||
|
||||
This fully installs the project, including installing any dependencies and running the build.
|
||||
|
||||
## Building node-veilid
|
||||
|
||||
If you have already installed the project and only want to run the build, run:
|
||||
|
||||
```sh
|
||||
$ npm run build
|
||||
```
|
||||
|
||||
This command uses the [cargo-cp-artifact](https://github.com/neon-bindings/cargo-cp-artifact) utility to run the Rust build and copy the built library into `./index.node`.
|
||||
|
||||
## Exploring node-veilid
|
||||
|
||||
After building node-veilid, you can explore its exports at the Node REPL:
|
||||
|
||||
```sh
|
||||
$ npm install
|
||||
$ node
|
||||
> require('.').hello()
|
||||
"hello node"
|
||||
```
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm install`
|
||||
|
||||
Installs the project, including running `npm run build`.
|
||||
|
||||
### `npm build`
|
||||
|
||||
Builds the Node addon (`index.node`) from source.
|
||||
|
||||
Additional [`cargo build`](https://doc.rust-lang.org/cargo/commands/cargo-build.html) arguments may be passed to `npm build` and `npm build-*` commands. For example, to enable a [cargo feature](https://doc.rust-lang.org/cargo/reference/features.html):
|
||||
|
||||
```
|
||||
npm run build -- --feature=beetle
|
||||
```
|
||||
|
||||
#### `npm build-debug`
|
||||
|
||||
Alias for `npm build`.
|
||||
|
||||
#### `npm build-release`
|
||||
|
||||
Same as [`npm build`](#npm-build) but, builds the module with the [`release`](https://doc.rust-lang.org/cargo/reference/profiles.html#release) profile. Release builds will compile slower, but run faster.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Runs the unit tests by calling `cargo test`. You can learn more about [adding tests to your Rust code](https://doc.rust-lang.org/book/ch11-01-writing-tests.html) from the [Rust book](https://doc.rust-lang.org/book/).
|
||||
|
||||
## Project Layout
|
||||
|
||||
The directory structure of this project is:
|
||||
|
||||
```
|
||||
node-veilid/
|
||||
├── Cargo.toml
|
||||
├── README.md
|
||||
├── index.node
|
||||
├── package.json
|
||||
├── src/
|
||||
| └── lib.rs
|
||||
└── target/
|
||||
```
|
||||
|
||||
### Cargo.toml
|
||||
|
||||
The Cargo [manifest file](https://doc.rust-lang.org/cargo/reference/manifest.html), which informs the `cargo` command.
|
||||
|
||||
### README.md
|
||||
|
||||
This file.
|
||||
|
||||
### index.node
|
||||
|
||||
The Node addon—i.e., a binary Node module—generated by building the project. This is the main module for this package, as dictated by the `"main"` key in `package.json`.
|
||||
|
||||
Under the hood, a [Node addon](https://nodejs.org/api/addons.html) is a [dynamically-linked shared object](https://en.wikipedia.org/wiki/Library_(computing)#Shared_libraries). The `"build"` script produces this file by copying it from within the `target/` directory, which is where the Rust build produces the shared object.
|
||||
|
||||
### package.json
|
||||
|
||||
The npm [manifest file](https://docs.npmjs.com/cli/v7/configuring-npm/package-json), which informs the `npm` command.
|
||||
|
||||
### src/
|
||||
|
||||
The directory tree containing the Rust source code for the project.
|
||||
|
||||
### src/lib.rs
|
||||
|
||||
The Rust library's main module.
|
||||
|
||||
### target/
|
||||
|
||||
Binary artifacts generated by the Rust build.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Neon, see the [Neon documentation](https://neon-bindings.com).
|
||||
|
||||
To learn more about Rust, see the [Rust documentation](https://www.rust-lang.org).
|
||||
|
||||
To learn more about Node, see the [Node documentation](https://nodejs.org).
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "node-veilid",
|
||||
"version": "0.1.0",
|
||||
"description": "Node.js bindings for Veilid",
|
||||
"main": "index.node",
|
||||
"scripts": {
|
||||
"build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics",
|
||||
"build-debug": "npm run build --",
|
||||
"build-release": "npm run build -- --release",
|
||||
"install": "npm run build-release",
|
||||
"test": "cargo test",
|
||||
"exp": "npm run build && echo '-----' && node test.js && echo '-----'"
|
||||
},
|
||||
"author": "Sven Slootweg <admin@cryto.net>",
|
||||
"license": "WTFPL OR CC0-1.0",
|
||||
"devDependencies": {
|
||||
"cargo-cp-artifact": "^0.1"
|
||||
},
|
||||
"keywords": [
|
||||
"veilid"
|
||||
]
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
let
|
||||
nixpkgs = import <nixpkgs> {};
|
||||
unstable = (import (fetchTarball https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz) {});
|
||||
in
|
||||
with nixpkgs;
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "node-veilid-dev";
|
||||
|
||||
buildInputs = [
|
||||
unstable.capnproto # 1.0.1
|
||||
unstable.protobuf # 3.24.3
|
||||
];
|
||||
}
|
@ -0,0 +1,213 @@
|
||||
use std::{path::PathBuf, sync::{Arc, mpsc}, io};
|
||||
|
||||
use neon::{prelude::*, result::Throw};
|
||||
use once_cell::sync::OnceCell;
|
||||
use thiserror::Error;
|
||||
use veilid_core::{CryptoKind, CryptoTyped, KeyPair, CRYPTO_KIND_VLD0, VeilidAPI, api_startup_json, VeilidAPIError};
|
||||
|
||||
// #[derive(Error, Debug)]
|
||||
// pub enum BindingsError {
|
||||
// #[error(transparent)]
|
||||
// VeilidAPIError (#[from] VeilidAPIError),
|
||||
// #[error(transparent)]
|
||||
// Throw (#[from] Throw)
|
||||
// }
|
||||
|
||||
fn make_runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static tokio::runtime::Runtime> {
|
||||
static RUNTIME: OnceCell<tokio::runtime::Runtime> = OnceCell::new();
|
||||
|
||||
RUNTIME.get_or_try_init(||
|
||||
tokio::runtime::Runtime::new()
|
||||
.or_else(|error| cx.throw_error(error.to_string()))
|
||||
)
|
||||
}
|
||||
|
||||
// fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
|
||||
// Ok(cx.string(veilid_core::veilid_version_string()))
|
||||
// }
|
||||
|
||||
// fn async_hello(mut cx: FunctionContext) -> JsResult<JsPromise> {
|
||||
// let runtime = make_runtime(&mut cx)?;
|
||||
// let (deferred, promise) = cx.promise();
|
||||
// let channel = cx.channel();
|
||||
|
||||
// runtime.spawn(async move {
|
||||
// let result = veilid_core::veilid_version_string();
|
||||
|
||||
// deferred.settle_with(&channel, |mut cx| {
|
||||
// Ok(cx.string(result))
|
||||
// });
|
||||
// });
|
||||
|
||||
// Ok(promise)
|
||||
// }
|
||||
|
||||
// CryptoKind
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct NoFinalize<V>(V);
|
||||
|
||||
impl<V> Finalize for NoFinalize<V> {
|
||||
fn finalize<'a, C: Context<'a>>(self, _: &mut C) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> NoFinalize<V> {
|
||||
fn unbox(self) -> V {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
// trait FunctionContextExt {
|
||||
// fn simple_boxed<U: Send>(&mut self, value: U) -> Handle<JsBox<NoFinalize<U>>>;
|
||||
// }
|
||||
|
||||
// impl<'a> FunctionContextExt for FunctionContext<'a> {
|
||||
// fn simple_boxed<U: Send>(&mut self, value: U) -> Handle<JsBox<NoFinalize<U>>> {
|
||||
// self.boxed(NoFinalize(value))
|
||||
// }
|
||||
// }
|
||||
|
||||
type BoxedKeypair = JsBox<NoFinalize<CryptoTyped<KeyPair>>>;
|
||||
type BoxedCryptoKind = JsBox<NoFinalize<CryptoKind>>;
|
||||
type BoxedPathBuf = JsBox<NoFinalize<PathBuf>>;
|
||||
type BoxedLibraryCore = JsBox<LibraryCore>;
|
||||
|
||||
// Type utility functions
|
||||
fn make_path(mut cx: FunctionContext) -> JsResult<BoxedPathBuf> {
|
||||
let path_string = cx.argument
|
||||
::<JsString>(0)?
|
||||
.value(&mut cx);
|
||||
|
||||
Ok(cx.boxed(NoFinalize(PathBuf::from(path_string))))
|
||||
}
|
||||
|
||||
// Library API
|
||||
fn crypto_generate_keypair(mut cx: FunctionContext) -> JsResult<BoxedKeypair> {
|
||||
let crypto_kind = cx.argument
|
||||
::<BoxedCryptoKind>(0)?
|
||||
.unbox();
|
||||
|
||||
let keypair = veilid_core::Crypto::generate_keypair(crypto_kind)
|
||||
.or_else(|err| cx.throw_error(err.to_string()))?;
|
||||
|
||||
Ok(cx.boxed(NoFinalize(keypair)))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LibraryCore {
|
||||
api: VeilidAPI
|
||||
}
|
||||
|
||||
impl Finalize for LibraryCore {
|
||||
fn finalize<'a, C: Context<'a>>(self, _: &mut C) {
|
||||
// FIXME
|
||||
}
|
||||
}
|
||||
|
||||
impl LibraryCore {
|
||||
fn from_json(mut cx: FunctionContext,) -> JsResult<JsPromise> {
|
||||
// 0: update_callback
|
||||
// 1: json_config
|
||||
|
||||
let update_callback = cx.argument
|
||||
::<JsFunction>(0)?
|
||||
.root(&mut cx); // To allow sending to a different thread
|
||||
|
||||
let json_config = cx.argument
|
||||
::<JsString>(1)?
|
||||
.value(&mut cx);
|
||||
|
||||
let runtime = make_runtime(&mut cx)?;
|
||||
let (deferred, promise) = cx.promise();
|
||||
let startup_channel = cx.channel();
|
||||
let (update_sender, update_receiver) = flume::unbounded();
|
||||
|
||||
//
|
||||
|
||||
let _startup_task = runtime.spawn(async move {
|
||||
let update_handler = Arc::new(move |update| {
|
||||
let result = update_sender.send(update);
|
||||
|
||||
if let Err(error) = result {
|
||||
// FIXME: Don't just dump this to console!
|
||||
println!("failed to send veilid update from callback: {:?}", error.into_inner());
|
||||
}
|
||||
});
|
||||
|
||||
let core_result = async {
|
||||
let api = api_startup_json(update_handler, json_config).await?;
|
||||
let core = LibraryCore { api: api };
|
||||
Ok(core)
|
||||
}.await;
|
||||
|
||||
deferred.settle_with(&startup_channel, |mut cx| {
|
||||
let core = core_result.or_else(|err: VeilidAPIError| cx.throw_error(err.to_string()))?;
|
||||
|
||||
let object = cx.empty_object();
|
||||
|
||||
let _ref = cx.boxed(core);
|
||||
object.set(&mut cx, "_ref", _ref)?;
|
||||
|
||||
let test = JsFunction::new(&mut cx, LibraryCore::test)?;
|
||||
object.set(&mut cx, "test", test)?;
|
||||
|
||||
let test2 = JsFunction::new(&mut cx, LibraryCore::test2)?;
|
||||
object.set(&mut cx, "test2", test2)?;
|
||||
|
||||
Ok(object)
|
||||
});
|
||||
});
|
||||
|
||||
let update_channel = cx.channel();
|
||||
|
||||
let _calling_task = runtime.spawn(async move {
|
||||
loop {
|
||||
let update = update_receiver.recv_async().await;
|
||||
let update_callback = update_callback.clone(&mut cx);
|
||||
|
||||
update_channel.send(move |mut cx| {
|
||||
update_callback
|
||||
.into_inner(&mut cx) // Unpack original callback
|
||||
.call_with(&mut cx)
|
||||
.arg(cx.string(format!("{:?}", update)))
|
||||
.apply::<JsUndefined, _>(&mut cx);
|
||||
|
||||
Ok(())
|
||||
}).join(); // FIXME: This will probably block?
|
||||
}
|
||||
});
|
||||
|
||||
Ok(promise)
|
||||
}
|
||||
|
||||
fn test(mut cx: FunctionContext) -> JsResult<JsString> {
|
||||
let this = cx.this()
|
||||
.get_value(&mut cx, "_ref")?
|
||||
.downcast_or_throw::<BoxedLibraryCore, _>(&mut cx)?;
|
||||
|
||||
println!("{:?}", this);
|
||||
Ok(cx.string(format!("{:?}", &this.api)))
|
||||
}
|
||||
|
||||
fn test2(mut cx: FunctionContext) -> JsResult<JsString> {
|
||||
let this = cx.this()
|
||||
.get_value(&mut cx, "_ref")?
|
||||
.downcast_or_throw::<BoxedLibraryCore, _>(&mut cx)?;
|
||||
|
||||
println!("{:?}", this);
|
||||
Ok(cx.string(format!("{:?}", &this.api)))
|
||||
}
|
||||
}
|
||||
|
||||
#[neon::main]
|
||||
fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
||||
cx.export_function("makePath", make_path)?;
|
||||
cx.export_function("crypto_generateKeypair", crypto_generate_keypair)?;
|
||||
cx.export_function("libraryCore_fromJSON", LibraryCore::from_json)?;
|
||||
|
||||
let crypto_kind_vld0 = cx.boxed(NoFinalize(CRYPTO_KIND_VLD0));
|
||||
cx.export_value("CRYPTO_KIND_VLD0", crypto_kind_vld0)?;
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
|
||||
const veilid = require("./");
|
||||
|
||||
(async()=>{
|
||||
// let path = veilid.makePath("/tmp/test");
|
||||
// let keypair = veilid.crypto_generateKeypair(veilid.CRYPTO_KIND_VLD0);
|
||||
let core = veilid.libraryCore_fromJSON();
|
||||
console.log("core", core);
|
||||
console.log("test", core.test());
|
||||
console.log("test2", core.test2());
|
||||
})();
|
@ -0,0 +1,8 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
cargo-cp-artifact@^0.1:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/cargo-cp-artifact/-/cargo-cp-artifact-0.1.8.tgz#353814f49f6aa76601a4bcb3ea5f3071180b90de"
|
||||
integrity sha512-3j4DaoTrsCD1MRkTF2Soacii0Nx7UHCce0EwUf4fHnggwiE4fbmF2AbnfzayR36DF8KGadfh7M/Yfy625kgPlA==
|
Loading…
Reference in New Issue