#![deny(unsafe_code)]
pub use techne_lexicon_check as check;
pub mod put;
mod renames;
pub mod verify;
pub mod versions;
#[cfg(target_arch = "wasm32")]
#[allow(unsafe_code)]
mod wasm;
use serde::de::DeserializeOwned;
use serde_json::{json, Value};
fn arg<T: DeserializeOwned>(args: &Value, name: &str) -> Result<T, String> {
T::deserialize(args.get(name).unwrap_or(&Value::Null)).map_err(|e| format!("{name}: {e}"))
}
fn str_arg<'a>(args: &'a Value, name: &str) -> Result<&'a str, String> {
args.get(name).and_then(Value::as_str).ok_or_else(|| format!("{name}: expected a string"))
}
fn now_arg(args: &Value, function: &str) -> Result<u64, String> {
arg::<Option<u64>>(args, "now")?.ok_or_else(|| {
format!("{function}: now (unix seconds) is required: the WebAssembly has no clock, and an old name's lexicon depends on the day")
})
}
pub fn call(function: &str, args: &Value) -> Result<Value, String> {
let out = match function {
"eventState" => {
let data: versions::GetVersions = arg(args, "data")?;
let picked: Option<String> = arg(args, "picked")?;
json!(versions::event_state(&data, picked.as_deref()))
}
"comparable" => versions::shown_as(str_arg(args, "field")?, args.get("value").unwrap_or(&Value::Null)),
"validate" => {
let collection = str_arg(args, "collection")?;
let problems = check::validate(collection, args.get("body").unwrap_or(&Value::Null)).err().unwrap_or_default();
json!({"valid": problems.is_empty(), "problems": problems, "publicHome": check::has_public_home(collection)})
}
"decide" => {
let claims: verify::Claims = arg(args, "claims")?;
let body = args.get("body").unwrap_or(&Value::Null);
let space: Option<String> = arg(args, "space")?;
let version = check::Version {
collection: str_arg(args, "collection")?,
author: str_arg(args, "author")?,
rkey: str_arg(args, "rkey")?,
body,
space: space.as_deref(),
};
let first_seen: Option<String> = arg(args, "firstSeen")?;
json!(check::decide(&version, first_seen.as_deref().unwrap_or_default(), &claims))
}
"put" => {
let records: Vec<put::Record> = arg(args, "records")?;
json!(put::plan_at(str_arg(args, "repo")?, records, now_arg(args, "put")?)?)
}
"putPlan" => {
let records: Vec<put::Record> = arg(args, "records")?;
let deletes: Option<Vec<put::DeleteRecord>> = arg(args, "deletes")?;
let (repo, deletes) = (str_arg(args, "repo")?, deletes.unwrap_or_default());
json!(put::plan_with_deletes_at(repo, records, deletes, now_arg(args, "putPlan")?)?)
}
"verify" => {
let record: verify::Record = arg(args, "record")?;
let claims: verify::Claims = arg(args, "claims")?;
json!(verify::verify(&record, &claims))
}
other => return Err(format!("no SDK function {other}")),
};
Ok(out)
}
#[cfg(feature = "crypto")]
pub mod account;
#[cfg(feature = "crypto")]
pub mod did;
#[cfg(feature = "crypto")]
pub mod labels;
#[cfg(feature = "crypto")]
pub mod service_jwt;
#[cfg(feature = "crypto")]
pub mod sync;
#[cfg(feature = "net")]
pub mod fetch;
#[cfg(feature = "net")]
pub mod service_auth;
#[cfg(feature = "net")]
pub mod session;
#[cfg(feature = "crypto")]
pub mod auth {
pub use crate::service_jwt::{mint_service_jwt, precheck_claims, verify_service_jwt, ServiceJwtError, VerifiedClaims};
#[cfg(feature = "net")]
pub use crate::did::{CommonDidKeyResolver, DidKeyResolver, InMemoryDidKeyResolver};
#[cfg(feature = "net")]
pub use crate::service_auth::{AuthError, InProcessJtiCache, JtiCache, ServiceAuth};
}
#[cfg(feature = "tap")]
pub mod tap;