feat(pubky): add wasm mod

This commit is contained in:
nazeh
2024-07-24 22:26:32 +03:00
parent 979882b443
commit ae4a34c511
13 changed files with 280 additions and 6 deletions

5
Cargo.lock generated
View File

@@ -1252,6 +1252,8 @@ version = "0.1.0"
dependencies = [
"bytes",
"flume",
"futures",
"js-sys",
"pkarr",
"pubky-common",
"pubky_homeserver",
@@ -1259,6 +1261,9 @@ dependencies = [
"tokio",
"ureq",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]

3
js/pubky/README.md Normal file
View File

@@ -0,0 +1,3 @@
# Pubky
This is a JavaScript implementation of a Pubky protocol client-side tools.

View File

@@ -0,0 +1,9 @@
import { PubkyClient } from '../src/index.js'
main()
async function main() {
let client = new PubkyClient()
console.log(client)
}

5
js/pubky/src/index.js Normal file
View File

@@ -0,0 +1,5 @@
// import { PubkyClient as _PubkyClient } from './lib/client.js'
// import { PubkyError as _PubkyError } from './lib/error.js'
//
// export const PubkyClient = _PubkyClient;
// export const PubkyError = _PubkyError;

102
js/pubky/src/lib/client.js Normal file
View File

@@ -0,0 +1,102 @@
import Pkarr, { SignedPacket } from 'pkarr'
import { URL } from 'url'
// import { AuthnSignature, crypto } from '@pubky/common'
import * as crypto from '../common/crypto.js'
// import { Pubky } from './pubky.js'
import fetch from './fetch.js'
const DEFAULT_PKARR_RELAY = new URL('https://relay.pkarr.org')
export class PubkyClient {
// TODO: use DHT in nodejs
#pkarrRelay
crypto = crypto
static crypto = crypto
/**
* @param {object} [options={}]
* @param {URL} [options.pkarrRelay]
*
* @param {{relay: string, bootstrap: Array<{host: string, port: number}>}} [options.testnet]
*/
constructor(options = {}) {
this.#pkarrRelay = options.pkarrRelay || DEFAULT_PKARR_RELAY
}
/**
* Publish the SVCB record for `_pubky.<public_key>`.
* @param {crypto.KeyPair} keypair
* @param {String} host
*/
async publishPubkyHomeserver(keypair, host) {
let existing = await (async () => {
try {
return (await Pkarr.relayGet(this.#pkarrRelay.toString(), keypair.publicKey().bytes)).packet()
} catch (error) {
return {
id: 0,
type: 'response',
flags: 0,
answers: []
}
}
})();
let answers = [
];
for (let answer of existing.answers) {
if (!answer.name.startsWith("_pubky")) {
answers.push(answer)
}
}
let signedPacket = SignedPacket.fromPacket(keypair, {
id: 0,
type: 'response',
flags: 0,
answers: [
...answers,
{
name: '_pubky.', type: 'SVCB', ttl: 7200, data:
Buffer.from(
)
}
]
})
// let mut packet = Packet:: new_reply(0);
//
// if let Some(existing) = self.pkarr.resolve(& keypair.public_key()) ? {
// for answer in existing.packet().answers.iter().cloned() {
// if !answer.name.to_string().starts_with("_pubky") {
// packet.answers.push(answer.into_owned())
// }
// }
// }
//
// let svcb = SVCB::new (0, host.try_into() ?);
//
// packet.answers.push(pkarr:: dns:: ResourceRecord:: new (
// "_pubky".try_into().unwrap(),
// pkarr:: dns:: CLASS:: IN,
// 60 * 60,
// pkarr:: dns:: rdata:: RData:: SVCB(svcb),
// ));
//
// let signed_packet = SignedPacket:: from_packet(keypair, & packet) ?;
//
// self.pkarr.publish(& signed_packet) ?;
}
}
export default PubkyClient

View File

@@ -0,0 +1 @@
export class PubkyError extends Error { }

View File

@@ -0,0 +1,11 @@
/* eslint-disable no-prototype-builtins */
const g =
(typeof globalThis !== 'undefined' && globalThis) ||
// eslint-disable-next-line no-undef
(typeof self !== 'undefined' && self) ||
// eslint-disable-next-line no-undef
(typeof global !== 'undefined' && global) ||
{}
// @ts-ignore
export default g.fetch

View File

@@ -0,0 +1,3 @@
import fetch from 'node-fetch-cache'
export default fetch

View File

@@ -3,16 +3,35 @@ name = "pubky"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
pkarr = "2.1.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
pubky-common = { version = "0.1.0", path = "../pubky-common" }
pkarr = "2.1.0"
ureq = { version = "2.10.0", features = ["cookies"] }
thiserror = "1.0.62"
url = "2.5.2"
flume = { version = "0.11.0", features = ["select", "eventual-fairness"], default-features = false }
bytes = "1.6.1"
[target.'cfg(target_arch = "wasm32")'.dependencies]
futures = "0.3.29"
js-sys = "0.3.69"
wasm-bindgen = "0.2.92"
wasm-bindgen-futures = "0.4.42"
web-sys = { version = "0.3.69", features = [
"console",
"Request",
"RequestInit",
"RequestMode",
"Response",
"Window",
] }
[dev-dependencies]
pubky_homeserver = { path = "../pubky-homeserver" }
tokio = "1.37.0"

View File

@@ -1,8 +1,30 @@
#![allow(unused)]
mod client;
mod client_async;
mod error;
macro_rules! if_not_wasm {
($($item:item)*) => {$(
#[cfg(not(target_arch = "wasm32"))]
$item
)*}
}
pub use client::PubkyClient;
pub use error::Error;
macro_rules! if_wasm {
($($item:item)*) => {$(
#[cfg(target_arch = "wasm32")]
$item
)*}
}
if_not_wasm! {
mod client;
mod client_async;
mod error;
pub use client::PubkyClient;
pub use error::Error;
}
if_wasm! {
mod wasm;
pub use wasm::{PubkyClient, Keypair};
}

6
pubky/src/wasm.rs Normal file
View File

@@ -0,0 +1,6 @@
mod keys;
mod client;
pub use client::PubkyClient;
pub use keys::Keypair;

73
pubky/src/wasm/client.rs Normal file
View File

@@ -0,0 +1,73 @@
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use web_sys::RequestMode;
use pkarr::PkarrRelayClient;
use super::Keypair;
#[wasm_bindgen]
pub struct Error {}
#[wasm_bindgen]
pub struct PubkyClient {
pkarr: PkarrRelayClient,
}
#[wasm_bindgen]
impl PubkyClient {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {
pkarr: PkarrRelayClient::default(),
}
}
/// Signup to a homeserver and update Pkarr accordingly.
///
/// The homeserver is a Pkarr domain name, where the TLD is a Pkarr public key
/// for example "pubky.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy"
#[wasm_bindgen]
pub fn signup(&self, secret_key: Keypair, homeserver: &str) -> Result<(), JsValue> {
// let (audience, mut url) = self.resolve_endpoint(homeserver)?;
// url.set_path(&format!("/{}", keypair.public_key()));
// let body = AuthnSignature::generate(keypair, &audience).as_bytes();
// fetch_base(url.to_string(), "PUT", body).await?;
// self.publish_pubky_homeserver(keypair, homeserver);
Ok(())
}
}
async fn fetch_base(
url: &String,
method: &str,
body: Option<Vec<u8>>,
) -> Result<web_sys::Response, JsValue> {
let mut opts = web_sys::RequestInit::new();
opts.method(method);
opts.mode(RequestMode::Cors);
if let Some(body) = body {
let body_bytes: &[u8] = &body;
let body_array: js_sys::Uint8Array = body_bytes.into();
let js_value: &JsValue = body_array.as_ref();
opts.body(Some(js_value));
}
let js_request = web_sys::Request::new_with_str_and_init(url, &opts)?;
// .map_err(|error| Error::JsError(error))?;
let window = web_sys::window().unwrap();
let response = JsFuture::from(window.fetch_with_request(&js_request)).await?;
// .map_err(|error| Error::JsError(error))?;
let response: web_sys::Response = response.dyn_into()?;
// .map_err(|error| Error::JsError(error))?
Ok(response)
}

15
pubky/src/wasm/keys.rs Normal file
View File

@@ -0,0 +1,15 @@
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Keypair(pkarr::Keypair);
#[wasm_bindgen]
impl Keypair {
#[wasm_bindgen]
pub fn from_secret_key(secret_key: js_sys::Uint8Array) -> Self {
let mut bytes = [0; 32];
secret_key.copy_to(&mut bytes);
Self(pkarr::Keypair::from_secret_key(&bytes))
}
}