1#![deny(unsafe_code)]
20
21pub use techne_lexicon_check as check;
22
23pub mod put;
24mod renames;
25pub mod verify;
26pub mod versions;
27
28#[cfg(target_arch = "wasm32")]
30#[allow(unsafe_code)]
31mod wasm;
32
33use serde::de::DeserializeOwned;
34use serde_json::{json, Value};
35
36fn arg<T: DeserializeOwned>(args: &Value, name: &str) -> Result<T, String> {
37 T::deserialize(args.get(name).unwrap_or(&Value::Null)).map_err(|e| format!("{name}: {e}"))
38}
39
40fn str_arg<'a>(args: &'a Value, name: &str) -> Result<&'a str, String> {
41 args.get(name).and_then(Value::as_str).ok_or_else(|| format!("{name}: expected a string"))
42}
43
44fn now_arg(args: &Value, function: &str) -> Result<u64, String> {
46 arg::<Option<u64>>(args, "now")?.ok_or_else(|| {
47 format!("{function}: now (unix seconds) is required: the WebAssembly has no clock, and an old name's lexicon depends on the day")
48 })
49}
50
51pub fn call(function: &str, args: &Value) -> Result<Value, String> {
53 let out = match function {
54 "eventState" => {
55 let data: versions::GetVersions = arg(args, "data")?;
56 let picked: Option<String> = arg(args, "picked")?;
57 json!(versions::event_state(&data, picked.as_deref()))
58 }
59 "comparable" => versions::shown_as(str_arg(args, "field")?, args.get("value").unwrap_or(&Value::Null)),
60 "validate" => {
61 let collection = str_arg(args, "collection")?;
62 let problems = check::validate(collection, args.get("body").unwrap_or(&Value::Null)).err().unwrap_or_default();
63 json!({"valid": problems.is_empty(), "problems": problems, "publicHome": check::has_public_home(collection)})
64 }
65 "decide" => {
66 let claims: verify::Claims = arg(args, "claims")?;
67 let body = args.get("body").unwrap_or(&Value::Null);
68 let space: Option<String> = arg(args, "space")?;
69 let version = check::Version {
70 collection: str_arg(args, "collection")?,
71 author: str_arg(args, "author")?,
72 rkey: str_arg(args, "rkey")?,
73 body,
74 space: space.as_deref(),
75 };
76 let first_seen: Option<String> = arg(args, "firstSeen")?;
77 json!(check::decide(&version, first_seen.as_deref().unwrap_or_default(), &claims))
78 }
79 "put" => {
83 let records: Vec<put::Record> = arg(args, "records")?;
84 json!(put::plan_at(str_arg(args, "repo")?, records, now_arg(args, "put")?)?)
85 }
86 "putPlan" => {
87 let records: Vec<put::Record> = arg(args, "records")?;
88 let deletes: Option<Vec<put::DeleteRecord>> = arg(args, "deletes")?;
89 let (repo, deletes) = (str_arg(args, "repo")?, deletes.unwrap_or_default());
90 json!(put::plan_with_deletes_at(repo, records, deletes, now_arg(args, "putPlan")?)?)
91 }
92 "verify" => {
93 let record: verify::Record = arg(args, "record")?;
94 let claims: verify::Claims = arg(args, "claims")?;
95 json!(verify::verify(&record, &claims))
96 }
97 other => return Err(format!("no SDK function {other}")),
98 };
99 Ok(out)
100}
101
102#[cfg(feature = "crypto")]
108pub mod account;
109#[cfg(feature = "crypto")]
110pub mod did;
111#[cfg(feature = "crypto")]
112pub mod labels;
113#[cfg(feature = "crypto")]
114pub mod service_jwt;
115#[cfg(feature = "crypto")]
116pub mod sync;
117
118#[cfg(feature = "net")]
119pub mod fetch;
120#[cfg(feature = "net")]
121pub mod service_auth;
122#[cfg(feature = "net")]
123pub mod session;
124
125#[cfg(feature = "crypto")]
128pub mod auth {
129 pub use crate::service_jwt::{mint_service_jwt, precheck_claims, verify_service_jwt, ServiceJwtError, VerifiedClaims};
130 #[cfg(feature = "net")]
131 pub use crate::did::{CommonDidKeyResolver, DidKeyResolver, InMemoryDidKeyResolver};
132 #[cfg(feature = "net")]
133 pub use crate::service_auth::{AuthError, InProcessJtiCache, JtiCache, ServiceAuth};
134}
135
136#[cfg(feature = "tap")]
139pub mod tap;