feat(client): Set Relays in the JS Client Config (#139)

* added js pubky client constructor config

* fixed bugs

* fmt and cargotoml

* moved wasm constructor order

* removed pubky-common reexport of client
This commit is contained in:
Severin Alexander Bühler
2025-05-23 18:32:37 +03:00
committed by GitHub
parent 9b8e1ab8c9
commit a485d8c2f4
27 changed files with 387 additions and 162 deletions

View File

@@ -114,10 +114,18 @@ jobs:
- name: Wait for testnet homeserver
run: |
timeout=180
count=0
until nc -zv 127.0.0.1 6288; do
echo "Waiting for testnet homeserver to be ready..."
if [ $count -ge $timeout ]; then
echo "Timeout: Testnet homeserver did not start within 3 minutes."
exit 1
fi
echo "Waiting for testnet homeserver to be ready... ($count/$timeout)"
sleep 1
count=$((count + 1))
done
echo "Testnet homeserver is ready."
- name: Run Tests (only node-js)
working-directory: pubky-client/pkg

64
Cargo.lock generated
View File

@@ -860,7 +860,6 @@ version = "0.1.0"
dependencies = [
"bytes",
"pkarr",
"pubky-common",
"pubky-testnet",
"reqwest",
"tokio",
@@ -1191,6 +1190,19 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "gloo-utils"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa"
dependencies = [
"js-sys",
"serde",
"serde_json",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "governor"
version = "0.8.0"
@@ -2400,10 +2412,13 @@ dependencies = [
"pkarr",
"pubky-common",
"reqwest",
"serde",
"serde-wasm-bindgen",
"thiserror 2.0.12",
"tokio",
"tracing",
"tracing-subscriber",
"tsify",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
@@ -3024,6 +3039,17 @@ dependencies = [
"toml",
]
[[package]]
name = "serde-wasm-bindgen"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "serde_bencode"
version = "0.2.4"
@@ -3054,6 +3080,17 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_derive_internals"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.139"
@@ -3724,6 +3761,31 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "tsify"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ec91b85e6c6592ed28636cb1dd1fac377ecbbeb170ff1d79f97aac5e38926d"
dependencies = [
"gloo-utils",
"serde",
"serde_json",
"tsify-macros",
"wasm-bindgen",
]
[[package]]
name = "tsify-macros"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a324606929ad11628a19206d7853807481dcaecd6c08be70a235930b8241955"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn",
]
[[package]]
name = "typenum"
version = "1.18.0"

View File

@@ -6,7 +6,6 @@ publish = false
[dependencies]
pubky-testnet = { workspace = true }
pubky-common = { workspace = true }
tokio = { version = "1.43.0", features = ["full", "test-util"] }
tracing-subscriber = "0.3.19"
pkarr = {workspace = true}

View File

@@ -1,5 +1,5 @@
use pkarr::Keypair;
use pubky_common::capabilities::{Capabilities, Capability};
use pubky_testnet::pubky_common::capabilities::{Capabilities, Capability};
use pubky_testnet::{
pubky_homeserver::{MockDataDir, SignupMode},
EphemeralTestnet, Testnet,

View File

@@ -1,12 +1,17 @@
[package]
name = "pubky"
description = "Pubky-Core Client"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
version = "0.5.0-rc.0"
edition = "2024"
authors = [
"SeverinAlexB <severin@synonym.to>",
"SHAcollision <shacollision@synonym.to>",
"Nuh <nuh@nuh.dev>"
]
license = "MIT"
homepage = "https://github.com/pubky/pubky-core"
repository = "https://github.com/pubky/pubky-core"
keywords = ["web", "dht", "dns", "decentralized", "identity"]
categories = [
"network-programming",
@@ -25,7 +30,7 @@ wasm-bindgen = "0.2.100"
url = "2.5.4"
bytes = "^1.10.0"
base64 = "0.22.1"
pkarr = { workspace = true, features = ["full"] }
pkarr = {version = "3.7.1", features = ["full"] }
cookie = "0.18.1"
tracing = "0.1.41"
cookie_store = { version = "0.21.1", default-features = false }
@@ -52,7 +57,9 @@ wasm-bindgen-futures = "0.4.50"
console_log = { version = "1.0.0", features = ["color"] }
log = "0.4.25"
gloo-timers = { version = "0.3", features = ["futures"] }
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
tsify = "0.5.5"
js-sys = "0.3.77"
web-sys = "0.3.77"
@@ -61,7 +68,7 @@ anyhow = "1.0.95"
futures-lite = "2.6.0"
tokio = "1.43.0"
tracing-subscriber = "0.3.19"
mainline = { workspace = true }
mainline = { version = "5.4.0" }
[build-dependencies]
cfg_aliases = "0.2.1"

View File

@@ -0,0 +1,35 @@
import test from 'tape'
import { Client } from '../index.cjs'
test('new Client() without config', async (t) => {
const client = new Client(); // Should always work
t.ok(client, "should create a client");
});
test('new Client() with config', async (t) => {
const client = new Client({
pkarr: {
relays: ['http://localhost:15412/relay'],
requestTimeout: 1000
},
userMaxRecordAge: 1000
});
t.ok(client, "should create a client");
});
test('new Client() partial config', async (t) => {
const client = new Client({
userMaxRecordAge: 1000
});
t.ok(client, "should create a client");
});
test('new Client() with faulty config', async (t) => {
t.throws(() => new Client({
userMaxRecordAge: 0 // Zero is invalid
}), "should throw an error");
});

View File

@@ -1,6 +1,6 @@
import test from 'tape'
import { Keypair } from '../index.cjs'
import { Keypair, PublicKey } from '../index.cjs'
test('generate keys from a seed', async (t) => {
const secretkey = Buffer.from('5aa93b299a343aa2691739771f2b5b85e740ca14c685793d67870f88fa89dc51', 'hex')
@@ -16,6 +16,18 @@ test('fromSecretKey error', async (t) => {
const secretkey = Buffer.from('5aa93b299a343aa2691739771f2b5b', 'hex')
t.throws(() => Keypair.fromSecretKey(null), /Expected secret_key to be an instance of Uint8Array/)
t.throws(() => Keypair.fromSecretKey(null), "Expected secret_key to be an instance of Uint8Array");
t.throws(() => Keypair.fromSecretKey(secretkey), /Expected secret_key to be 32 bytes, got 15/)
})
test('PublicKey from and toUint8Array', async (t) => {
const z32 = "gcumbhd7sqit6nn457jxmrwqx9pyymqwamnarekgo3xppqo6a19o";
const publicKey = PublicKey.from(z32)
t.is(publicKey.z32(), z32)
t.deepEqual(publicKey.toUint8Array(), Uint8Array.from([
51, 38, 176, 240, 125, 179, 171, 31,
8, 90, 223, 82, 245, 146, 142, 127,
218, 0, 45, 212, 194, 197, 130, 33,
70, 134, 94, 214, 186, 30, 196, 191
]));
})

View File

@@ -19,17 +19,18 @@ macro_rules! cross_debug {
}
pub mod native;
mod shared;
#[cfg(wasm_browser)]
mod wasm;
#[cfg(not(wasm_browser))]
pub use crate::native::Client;
pub use crate::native::{api::auth::AuthRequest, api::public::ListBuilder, ClientBuilder};
pub use crate::native::{ClientBuilder, api::auth::AuthRequest, api::public::ListBuilder};
#[cfg(wasm_browser)]
pub use native::Client as NativeClient;
#[cfg(wasm_browser)]
pub use wasm::Client;
pub use wasm::constructor::Client;
// Re-exports
pub use pkarr::{Keypair, PublicKey};

View File

@@ -1,6 +1,6 @@
use std::collections::HashMap;
use base64::{alphabet::URL_SAFE, engine::general_purpose::NO_PAD, Engine};
use base64::{Engine, alphabet::URL_SAFE, engine::general_purpose::NO_PAD};
use reqwest::{IntoUrl, Method, StatusCode};
use url::Url;
@@ -14,7 +14,7 @@ use pubky_common::{
use anyhow::Result;
use super::super::{internal::pkarr::PublishStrategy, Client};
use super::super::{Client, internal::pkarr::PublishStrategy};
use crate::handle_http_error;
impl Client {
@@ -398,11 +398,12 @@ impl AuthRequest {
}
}
#[cfg(not(wasm_browser))]
#[cfg(test)]
mod tests {
use pkarr::Keypair;
use crate::{native::internal::pkarr::PublishStrategy, Client};
use crate::{Client, native::internal::pkarr::PublishStrategy};
#[tokio::test]
async fn test_get_homeserver() {

View File

@@ -0,0 +1,4 @@
pub mod auth;
#[cfg(not(wasm_browser))]
pub mod http;
pub mod public;

View File

@@ -1,17 +1,7 @@
pub mod internal {
#[cfg(not(wasm_browser))]
pub mod cookies;
pub mod pkarr;
}
pub mod api {
pub mod auth;
#[cfg(not(wasm_browser))]
pub mod http;
pub mod public;
}
use std::fmt::Debug;
#[cfg(not(wasm_browser))]
use super::internal::cookies::CookieJar;
#[cfg(not(wasm_browser))]
use std::sync::Arc;
use std::time::Duration;
@@ -90,7 +80,7 @@ impl ClientBuilder {
let pkarr = self.pkarr.build()?;
#[cfg(not(wasm_browser))]
let cookie_store = Arc::new(internal::cookies::CookieJar::default());
let cookie_store = Arc::new(CookieJar::default());
// TODO: allow custom user agent, but force a Pubky user agent information
let user_agent = DEFAULT_USER_AGENT;
@@ -156,7 +146,7 @@ pub struct Client {
pub(crate) pkarr: pkarr::Client,
#[cfg(not(wasm_browser))]
pub(crate) cookie_store: std::sync::Arc<internal::cookies::CookieJar>,
pub(crate) cookie_store: std::sync::Arc<CookieJar>,
#[cfg(not(wasm_browser))]
pub(crate) icann_http: reqwest::Client,
@@ -183,6 +173,7 @@ impl Client {
}
}
#[cfg(not(wasm_browser))]
#[cfg(test)]
mod test {
use super::*;

View File

@@ -1,7 +1,7 @@
use std::{collections::HashMap, sync::RwLock};
use pkarr::PublicKey;
use reqwest::{cookie::CookieStore, header::HeaderValue, Response};
use reqwest::{Response, cookie::CookieStore, header::HeaderValue};
#[derive(Default, Debug)]
pub struct CookieJar {

View File

@@ -0,0 +1,3 @@
#[cfg(not(wasm_browser))]
pub mod cookies;
pub mod pkarr;

View File

@@ -1,20 +1,15 @@
use anyhow::Result;
use pkarr::{
Keypair, SignedPacket, Timestamp,
dns::rdata::{RData, SVCB},
errors::QueryError,
Keypair, SignedPacket, Timestamp,
};
use std::convert::TryInto;
use std::time::Duration;
use super::super::Client;
use crate::shared::sleep;
// sleep for native
#[cfg(not(wasm_browser))]
use tokio::time::sleep;
// sleep for wasm
#[cfg(wasm_browser)]
use gloo_timers::future::sleep;
use super::super::Client;
/// Helper returns true if this error (or any of its sources) is one of our
/// three recoverable `QueryError`s with simple retrial.
@@ -150,12 +145,13 @@ impl Client {
}
}
#[cfg(not(wasm_browser))]
#[cfg(test)]
mod tests {
use super::*;
use crate::Client;
use pkarr::dns::rdata::SVCB;
use pkarr::Keypair;
use pkarr::dns::rdata::SVCB;
#[tokio::test]
async fn test_extract_host_from_packet() -> Result<()> {

View File

@@ -0,0 +1,4 @@
pub mod api;
mod client;
pub mod internal;
pub use client::*;

View File

@@ -0,0 +1,2 @@
mod sleep;
pub use sleep::*;

View File

@@ -0,0 +1,14 @@
// sleep for native
#[cfg(not(wasm_browser))]
use tokio::time::sleep as inner_sleep;
// sleep for wasm
#[cfg(wasm_browser)]
use gloo_timers::future::sleep as inner_sleep;
use std::time::Duration;
/// Sleep for the given duration.
/// Works on both native and wasm.
pub async fn sleep(duration: Duration) {
inner_sleep(duration).await;
}

View File

@@ -4,12 +4,15 @@ use url::Url;
use pubky_common::capabilities::Capabilities;
use crate::wasm::wrappers::{
keys::{Keypair, PublicKey},
session::Session,
use crate::wasm::{
js_result::JsResult,
wrappers::{
keys::{Keypair, PublicKey},
session::Session,
},
};
use super::super::Client;
use super::super::constructor::Client;
use wasm_bindgen::prelude::*;
@@ -25,7 +28,7 @@ impl Client {
keypair: &Keypair,
homeserver: &PublicKey,
signup_token: Option<String>,
) -> Result<Session, JsValue> {
) -> JsResult<Session> {
Ok(Session(
self.0
.signup(
@@ -43,7 +46,7 @@ impl Client {
/// Returns [Session] or `None` (if received `404 NOT_FOUND`),
/// or throws the received error if the response has any other `>=400` status code.
#[wasm_bindgen]
pub async fn session(&self, pubky: &PublicKey) -> Result<Option<Session>, JsValue> {
pub async fn session(&self, pubky: &PublicKey) -> JsResult<Option<Session>> {
self.0
.session(pubky.as_inner())
.await
@@ -53,7 +56,7 @@ impl Client {
/// Signout from a homeserver.
#[wasm_bindgen]
pub async fn signout(&self, pubky: &PublicKey) -> Result<(), JsValue> {
pub async fn signout(&self, pubky: &PublicKey) -> JsResult<()> {
self.0
.signout(pubky.as_inner())
.await
@@ -62,7 +65,7 @@ impl Client {
/// Signin to a homeserver using the root Keypair.
#[wasm_bindgen]
pub async fn signin(&self, keypair: &Keypair) -> Result<(), JsValue> {
pub async fn signin(&self, keypair: &Keypair) -> JsResult<()> {
self.0
.signin(keypair.as_inner())
.await
@@ -76,7 +79,7 @@ impl Client {
///
/// Returns a [AuthRequest]
#[wasm_bindgen(js_name = "authRequest")]
pub fn auth_request(&self, relay: &str, capabilities: &str) -> Result<AuthRequest, JsValue> {
pub fn auth_request(&self, relay: &str, capabilities: &str) -> JsResult<AuthRequest> {
let auth_request = self
.0
.auth_request(
@@ -91,11 +94,7 @@ impl Client {
/// Sign an [pubky_common::auth::AuthToken], encrypt it and send it to the
/// source of the pubkyauth request url.
#[wasm_bindgen(js_name = "sendAuthToken")]
pub async fn send_auth_token(
&self,
keypair: &Keypair,
pubkyauth_url: &str,
) -> Result<(), JsValue> {
pub async fn send_auth_token(&self, keypair: &Keypair, pubkyauth_url: &str) -> JsResult<()> {
let pubkyauth_url: Url = pubkyauth_url.try_into().map_err(|_| "Invalid relay Url")?;
self.0
@@ -110,14 +109,12 @@ impl Client {
/// Looks up the pkarr packet for the given public key and returns the content of the first `_pubky` SVCB record.
/// Throws an error if no homeserver is found.
#[wasm_bindgen(js_name = "getHomeserver")]
pub async fn get_homeserver(&self, public_key: &PublicKey) -> Result<PublicKey, JsValue> {
let val = self.0.get_homeserver(public_key.as_inner()).await;
if val.is_none() {
return Err(JsValue::from_str("No homeserver found"));
}
let val = val.unwrap();
let js_val = JsValue::from_str(val.as_str());
PublicKey::try_from(js_val)
pub async fn get_homeserver(&self, public_key: &PublicKey) -> JsResult<PublicKey> {
let hs_z32 = match self.0.get_homeserver(public_key.as_inner()).await {
Some(hs_z32) => hs_z32,
None => return Err(JsValue::from_str("No homeserver found")),
};
PublicKey::try_from(hs_z32)
}
/// Republish the user's PKarr record pointing to their homeserver.
@@ -133,15 +130,11 @@ impl Client {
/// republishing. On a failed signin due to homeserver resolution failure, a key
/// manager should always attempt to republish the last known homeserver.
#[wasm_bindgen(js_name = "republishHomeserver")]
pub async fn republish_homeserver(
&self,
keypair: &Keypair,
host: &PublicKey,
) -> Result<(), JsValue> {
pub async fn republish_homeserver(&self, keypair: &Keypair, host: &PublicKey) -> JsResult<()> {
self.0
.republish_homeserver(keypair.as_inner(), host.as_inner())
.await
.map_err(|e| JsValue::from_str(&e.to_string()));
.map_err(|e| JsValue::from_str(&e.to_string()))?;
Ok(())
}
@@ -167,7 +160,7 @@ impl AuthRequest {
///
/// Otherwise it will throw an error.
#[wasm_bindgen]
pub async fn response(&self) -> Result<PublicKey, JsValue> {
pub async fn response(&self) -> JsResult<PublicKey> {
self.0
.response()
.await

View File

@@ -8,10 +8,12 @@ use reqwest::{IntoUrl, Method, RequestBuilder, Url};
use futures_lite::StreamExt;
use pkarr::extra::endpoints::Endpoint;
use pkarr::PublicKey;
use pkarr::extra::endpoints::Endpoint;
use super::super::Client;
use crate::wasm::js_result::JsResult;
use super::super::constructor::Client;
#[wasm_bindgen]
impl Client {
@@ -20,7 +22,7 @@ impl Client {
&self,
url: &str,
request_init: Option<RequestInit>,
) -> Result<js_sys::Promise, JsValue> {
) -> JsResult<js_sys::Promise> {
let mut url: Url = url.try_into().map_err(|err| {
JsValue::from_str(&format!("pubky::Client::fetch(): Invalid `url`; {:?}", err))
})?;

View File

@@ -0,0 +1,4 @@
pub mod auth;
pub mod http;
pub mod public;
pub mod recovery_file;

View File

@@ -3,7 +3,9 @@
use js_sys::Array;
use wasm_bindgen::prelude::*;
use super::super::Client;
use crate::wasm::js_result::JsResult;
use super::super::constructor::Client;
#[wasm_bindgen]
impl Client {
@@ -23,7 +25,7 @@ impl Client {
reverse: Option<bool>,
limit: Option<u16>,
shallow: Option<bool>,
) -> Result<Array, JsValue> {
) -> JsResult<Array> {
// TODO: try later to return Vec<String> from async function.
if let Some(cursor) = cursor {

View File

@@ -1,7 +1,7 @@
use js_sys::Uint8Array;
use wasm_bindgen::prelude::{wasm_bindgen, JsValue};
use wasm_bindgen::prelude::{JsValue, wasm_bindgen};
use crate::wasm::wrappers::keys::Keypair;
use crate::wasm::{js_result::JsResult, wrappers::keys::Keypair};
/// Create a recovery file of the `keypair`, containing the secret key encrypted
/// using the `passphrase`.
@@ -15,7 +15,7 @@ pub fn create_recovery_file(keypair: &Keypair, passphrase: &str) -> Uint8Array {
/// Create a recovery file of the `keypair`, containing the secret key encrypted
/// using the `passphrase`.
#[wasm_bindgen(js_name = "decryptRecoveryFile")]
pub fn decrypt_recovery_file(recovery_file: &[u8], passphrase: &str) -> Result<Keypair, JsValue> {
pub fn decrypt_recovery_file(recovery_file: &[u8], passphrase: &str) -> JsResult<Keypair> {
pubky_common::recovery_file::decrypt_recovery_file(recovery_file, passphrase)
.map(Keypair::from)
.map_err(|e| JsValue::from_str(&e.to_string()))

View File

@@ -0,0 +1,126 @@
use std::{num::NonZeroU64, time::Duration};
use serde::{Deserialize, Serialize};
use tsify::Tsify;
use wasm_bindgen::prelude::*;
use super::js_result::JsResult;
static TESTNET_RELAYS: [&str; 1] = ["http://localhost:15411/"];
// ------------------------------------------------------------------------------------------------
// JS style config objects for the client.
// ------------------------------------------------------------------------------------------------
/// Pkarr Config
#[derive(Tsify, Serialize, Deserialize, Debug)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[serde(rename_all = "camelCase")]
pub struct PkarrConfig {
/// The list of relays to access the DHT with.
pub(crate) relays: Option<Vec<String>>,
/// The timeout for DHT requests in milliseconds.
/// Default is 2000ms.
pub(crate) request_timeout: Option<NonZeroU64>,
}
/// Pubky Client Config
#[derive(Tsify, Serialize, Deserialize, Debug)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[serde(rename_all = "camelCase")]
pub struct PubkyClientConfig {
/// Configuration on how to access pkarr packets on the mainline DHT.
pub(crate) pkarr: Option<PkarrConfig>,
/// The maximum age of a record in seconds.
/// If the user pkarr record is older than this, it will be automatically refreshed.
pub(crate) user_max_record_age: Option<NonZeroU64>,
}
// ------------------------------------------------------------------------------------------------
// JS Client constructor
// ------------------------------------------------------------------------------------------------
#[wasm_bindgen]
pub struct Client(pub(crate) crate::NativeClient);
impl Default for Client {
fn default() -> Self {
Self::new(None).expect("No config constructor should be infallible")
}
}
#[wasm_bindgen]
impl Client {
#[wasm_bindgen(constructor)]
/// Create a new Pubky Client with an optional configuration.
pub fn new(config_opt: Option<PubkyClientConfig>) -> JsResult<Self> {
let mut builder = crate::NativeClient::builder();
let config = match config_opt {
Some(config) => config,
None => {
return Ok(Self(
builder
.build()
.expect("building a default NativeClient should be infallible"),
));
}
};
if let Some(pkarr) = config.pkarr {
// Set pkarr relays
if let Some(relays) = pkarr.relays {
let mut relay_set_error: Option<JsValue> = None;
builder.pkarr(|pkarr_builder| {
pkarr_builder.no_relays(); // Remove default pkarr config
if let Err(e) = pkarr_builder.relays(&relays) {
relay_set_error =
Some(JsValue::from_str(&format!("Failed to set relays. {}", e)));
}
pkarr_builder
});
if let Some(e) = relay_set_error {
return Err(e);
}
}
// Set pkarr timeout
if let Some(timeout) = pkarr.request_timeout {
builder.pkarr(|pkarr_builder| {
pkarr_builder.request_timeout(Duration::from_millis(timeout.get()));
pkarr_builder
});
}
}
// Set homeserver max record age
if let Some(max_record_age) = config.user_max_record_age {
builder.max_record_age(Duration::from_secs(max_record_age.get()));
}
let native_client = builder
.build()
.map_err(|e| JsValue::from_str(&format!("Failed to build client. {}", e)))?;
Ok(Self(native_client))
}
/// Create a client with with configurations appropriate for local testing:
/// - set Pkarr relays to `["http://localhost:15411"]` instead of default relay.
/// - transform `pubky://<pkarr public key>` to `http://<pkarr public key` instead of `https:`
/// and read the homeserver HTTP port from the [reserved service parameter key](pubky_common::constants::reserved_param_keys::HTTP_PORT)
#[wasm_bindgen]
pub fn testnet() -> Self {
let mut builder = crate::NativeClient::builder();
builder.pkarr(|builder| {
builder
.relays(&TESTNET_RELAYS)
.expect("testnet relays are valid urls")
});
let mut client = builder.build().expect("testnet build should be infallible");
client.testnet = true;
Self(client)
}
}

View File

@@ -0,0 +1,13 @@
use wasm_bindgen::JsValue;
/// Convenient type for functions that return a Err(JsValue).
///
/// fn method() -> JsResult<T> {
/// Ok(())
/// }
///
/// fn method() -> JsResult<T> {
/// Err(JsValue::from_str("error"))
/// }
///
pub type JsResult<T> = Result<T, JsValue>;

View File

@@ -1,61 +1,9 @@
use wasm_bindgen::prelude::*;
pub mod api {
pub mod auth;
pub mod http;
pub mod public;
pub mod recovery_file;
}
pub mod wrappers {
pub mod keys;
pub mod session;
}
static TESTNET_RELAYS: [&str; 1] = ["http://localhost:15411/"];
#[wasm_bindgen]
pub struct Client(crate::NativeClient);
impl Default for Client {
fn default() -> Self {
Self::new()
}
}
#[wasm_bindgen]
impl Client {
#[wasm_bindgen(constructor)]
/// Create Client with default Settings including default relays
pub fn new() -> Self {
Self(
crate::NativeClient::builder()
.build()
.expect("building a default NativeClient should be infallible"),
)
}
/// Create a client with with configurations appropriate for local testing:
/// - set Pkarr relays to `["http://localhost:15411"]` instead of default relay.
/// - transform `pubky://<pkarr public key>` to `http://<pkarr public key` instead of `https:`
/// and read the homeserver HTTP port from the [reserved service parameter key](pubky_common::constants::reserved_param_keys::HTTP_PORT)
#[wasm_bindgen]
pub fn testnet() -> Self {
let mut builder = crate::NativeClient::builder();
builder.pkarr(|builder| {
builder
.relays(&TESTNET_RELAYS)
.expect("testnet relays are valid urls")
});
let mut client = builder.build().expect("testnet build should be infallible");
client.testnet = true;
Self(client)
}
}
pub mod api;
pub mod constructor;
mod js_result;
pub mod wrappers;
#[wasm_bindgen(js_name = "setLogLevel")]
pub fn set_log_level(level: &str) -> Result<(), JsValue> {

View File

@@ -1,5 +1,7 @@
use wasm_bindgen::prelude::*;
use crate::wasm::js_result::JsResult;
#[wasm_bindgen]
pub struct Keypair(pkarr::Keypair);
@@ -13,26 +15,18 @@ impl Keypair {
/// Generate a [Keypair] from a secret key.
#[wasm_bindgen(js_name = "fromSecretKey")]
pub fn from_secret_key(secret_key: js_sys::Uint8Array) -> Result<Keypair, JsValue> {
if !js_sys::Uint8Array::instanceof(&secret_key) {
return Err("Expected secret_key to be an instance of Uint8Array".into());
}
let len = secret_key.byte_length();
if len != 32 {
return Err(format!("Expected secret_key to be 32 bytes, got {len}"))?;
}
let mut bytes = [0; 32];
secret_key.copy_to(&mut bytes);
Ok(Self(pkarr::Keypair::from_secret_key(&bytes)))
pub fn from_secret_key(secret_key: Vec<u8>) -> Result<Keypair, JsValue> {
let secret_len = secret_key.len();
let secret: [u8; 32] = secret_key
.try_into()
.map_err(|_| format!("Expected secret_key to be 32 bytes, got {}", secret_len))?;
Ok(Self(pkarr::Keypair::from_secret_key(&secret)))
}
/// Returns the secret key of this keypair.
#[wasm_bindgen(js_name = "secretKey")]
pub fn secret_key(&self) -> js_sys::Uint8Array {
self.0.secret_key().as_slice().into()
pub fn secret_key(&self) -> Vec<u8> {
self.0.secret_key().into_iter().collect()
}
/// Returns the [PublicKey] of this keypair.
@@ -59,10 +53,16 @@ pub struct PublicKey(pub(crate) pkarr::PublicKey);
#[wasm_bindgen]
impl PublicKey {
#[wasm_bindgen]
/// Convert the PublicKey to Uint8Array
pub fn to_uint8array(&self) -> js_sys::Uint8Array {
js_sys::Uint8Array::from(self.0.as_bytes().as_slice())
/// @deprecated Use `toUint8Array` instead
pub fn to_uint8array(&self) -> Vec<u8> {
self.0.as_bytes().to_vec()
}
/// Convert the PublicKey to Uint8Array
#[wasm_bindgen(js_name = "toUint8Array")]
pub fn to_uint8array2(&self) -> Vec<u8> {
self.0.as_bytes().to_vec()
}
#[wasm_bindgen]
@@ -73,12 +73,8 @@ impl PublicKey {
#[wasm_bindgen(js_name = "from")]
/// @throws
pub fn try_from(value: JsValue) -> Result<PublicKey, JsValue> {
let string = value
.as_string()
.ok_or("Couldn't create a PublicKey from this type of value")?;
Ok(PublicKey(pkarr::PublicKey::try_from(string).map_err(
pub fn try_from(value: String) -> JsResult<PublicKey> {
Ok(PublicKey(pkarr::PublicKey::try_from(value).map_err(
|_| "Couldn't create a PublicKey from this type of value",
)?))
}

View File

@@ -0,0 +1,2 @@
pub mod keys;
pub mod session;