diff --git a/Cargo.lock b/Cargo.lock index 9cc44f9..4c9a383 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1970,9 +1970,9 @@ dependencies = [ [[package]] name = "n0-future" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399e11dc3b0e8d9d65b27170d22f5d779d52d9bed888db70d7e0c2c7ce3dfc52" +checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794" dependencies = [ "cfg_aliases", "derive_more", @@ -2666,6 +2666,15 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3057,6 +3066,47 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +dependencies = [ + "indexmap 2.9.0", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + [[package]] name = "tower" version = "0.5.2" @@ -3381,7 +3431,10 @@ dependencies = [ "clap", "esquema-codegen", "hickory-resolver", + "miette", "n0-future", + "serde_html_form", + "tokio", "weaver-common", "weaver-workspace-hack", ] @@ -3415,6 +3468,7 @@ dependencies = [ "serde_json", "thiserror 2.0.12", "tokio", + "toml", "tracing", "weaver-workspace-hack", ] @@ -3817,6 +3871,15 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +[[package]] +name = "winnow" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" diff --git a/Cargo.toml b/Cargo.toml index 67bf32b..37e0100 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ owo-colors = { version = "4.2.0" } thiserror = "2.0" syntect = "5.2.0" jane-eyre = "0.6.12" -n0-future = "=0.1.2" +n0-future = "=0.1.3" tracing = { version = "0.1.41", default-features = false, features = ["std"] } lexicon_cid = { package = "cid", version = "0.10.1", features = [ "serde-codec", diff --git a/crates/weaver-cli/Cargo.toml b/crates/weaver-cli/Cargo.toml index bcfc5d0..0f8195f 100644 --- a/crates/weaver-cli/Cargo.toml +++ b/crates/weaver-cli/Cargo.toml @@ -10,6 +10,7 @@ clap = { version = "4.5", features = ["derive", "env", "cargo", "unicode"] } n0-future = { workspace = true } weaver-common = { path = "../weaver-common" } weaver-workspace-hack = { version = "0.1", path = "../weaver-workspace-hack" } +miette = { workspace = true, features = ["fancy"] } hickory-resolver = "0.24.1" esquema-codegen.workspace = true @@ -20,3 +21,8 @@ atrium-identity = "0.1.3" atrium-oauth = "0.1.1" atrium-xrpc-client = "0.5.13" atrium-lex = { workspace = true } + +serde_html_form = "0.2.7" + +# temp for testing +tokio = { version = "1.45.0", features = ["full"] } diff --git a/crates/weaver-cli/src/main.rs b/crates/weaver-cli/src/main.rs index f4a8df9..cda59f8 100644 --- a/crates/weaver-cli/src/main.rs +++ b/crates/weaver-cli/src/main.rs @@ -1,3 +1,58 @@ -//! A cli which will echo every input argument to stdout +use atrium_api::agent::Agent; +use atrium_api::xrpc::http::Uri; +use atrium_oauth::AuthorizeOptions; +use atrium_oauth::KnownScope; +use atrium_oauth::Scope; +use std::{ + error, + io::{BufRead, Write, stdin, stdout}, + sync::Arc, +}; -fn main() {} +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = weaver_common::oauth::default_oauth_client("http://127.0.0.1/callback")?; + println!( + "Authorization url: {}", + client + .authorize( + std::env::var("PDS_URL").unwrap_or(String::from("https://atproto.systems")), + AuthorizeOptions { + scopes: vec![ + Scope::Known(KnownScope::Atproto), + Scope::Known(KnownScope::TransitionGeneric) + ], + ..Default::default() + } + ) + .await? + ); + + print!("Redirected url: "); + stdout().lock().flush()?; + let mut url = String::new(); + stdin().lock().read_line(&mut url)?; + + let uri = url.trim().parse::()?; + let params = serde_html_form::from_str(uri.query().unwrap())?; + let (session, _) = client.callback(params).await?; + let agent = Agent::new(session); + let output = agent + .api + .app + .bsky + .feed + .get_timeline( + atrium_api::app::bsky::feed::get_timeline::ParametersData { + algorithm: None, + cursor: None, + limit: 3.try_into().ok(), + } + .into(), + ) + .await?; + for feed in &output.feed { + println!("{feed:?}"); + } + Ok(()) +} diff --git a/crates/weaver-common/Cargo.toml b/crates/weaver-common/Cargo.toml index d5d512e..5cc4f31 100644 --- a/crates/weaver-common/Cargo.toml +++ b/crates/weaver-common/Cargo.toml @@ -41,11 +41,12 @@ minijinja = { workspace = true, features = [ ] } -miette = { workspace = true, features = ["fancy"] } +miette = { workspace = true } owo-colors = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } hickory-resolver = "0.24.1" +toml = "0.8.22" diff --git a/crates/weaver-common/src/client.rs b/crates/weaver-common/src/client.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/weaver-common/src/config.rs b/crates/weaver-common/src/config.rs new file mode 100644 index 0000000..abe8d4d --- /dev/null +++ b/crates/weaver-common/src/config.rs @@ -0,0 +1,118 @@ +use atrium_api::agent::atp_agent::AtpSession; +use miette::Result; +use miette::miette; +use serde::{Deserialize, Serialize}; + +use std::future::Future; +use std::path::Path; +use std::path::PathBuf; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Config { + /// The base URL for the XRPC endpoint. + pub endpoint: String, + /// The session data. + pub session: Option, + /// The labelers header values. + pub labelers_header: Option>, + /// The proxy header for service proxying. + pub proxy_header: Option, +} + +impl Config { + /// Loads the configuration from the provided loader. + pub async fn load(loader: &impl Loader) -> Result { + loader + .load() + .await + .map_err(|_| miette!("Failed to load configuration")) + } + /// Saves the configuration using the provided saver. + pub async fn save(&self, saver: &impl Saver) -> Result<()> { + saver + .save(self) + .await + .map_err(|_| miette!("Failed to save configuration")) + } +} + +impl Default for Config { + /// Creates a new default configuration. + /// + /// The default configuration uses the base URL `https://bsky.social`. + fn default() -> Self { + Self { + endpoint: "https://atproto.systems".to_owned(), + session: None, + labelers_header: None, + proxy_header: None, + } + } +} + +/// The trait for loading configuration data. +pub trait Loader { + /// Loads the configuration data. + fn load( + &self, + ) -> impl Future< + Output = core::result::Result>, + > + Send; +} + +/// The trait for saving configuration data. +pub trait Saver { + /// Saves the configuration data. + fn save( + &self, + config: &Config, + ) -> impl Future< + Output = core::result::Result<(), Box>, + > + Send; +} + +/// An implementation of [`Loader`] and [`Saver`] that reads and writes a configuration file. +pub struct FileStore { + path: PathBuf, +} + +impl FileStore { + /// Create a new [`FileStore`] with the given path. + /// + /// This `FileStore` will read and write to the file at the given path. + /// [`Config`] data will be serialized and deserialized using the file extension. + /// By default, this supports only `.json` files. + pub fn new(path: impl AsRef) -> Self { + Self { + path: path.as_ref().to_path_buf(), + } + } +} + +impl Loader for FileStore { + async fn load( + &self, + ) -> core::result::Result> { + match self.path.extension().and_then(|ext| ext.to_str()) { + Some("json") => Ok(serde_json::from_str(&std::fs::read_to_string(&self.path)?)?), + Some("toml") => Ok(toml::from_str(&std::fs::read_to_string(&self.path)?)?), + _ => Err(miette!("Unsupported file format").into()), + } + } +} + +impl Saver for FileStore { + async fn save( + &self, + config: &Config, + ) -> core::result::Result<(), Box> { + match self.path.extension().and_then(|ext| ext.to_str()) { + Some("json") => Ok(std::fs::write( + &self.path, + serde_json::to_string_pretty(config)?, + )?), + Some("toml") => Ok(std::fs::write(&self.path, toml::to_string_pretty(config)?)?), + _ => Err(miette!("Unsupported file format").into()), + } + } +} diff --git a/crates/weaver-common/src/lexicons/record.rs b/crates/weaver-common/src/lexicons/record.rs index d58d8ee..82d6503 100644 --- a/crates/weaver-common/src/lexicons/record.rs +++ b/crates/weaver-common/src/lexicons/record.rs @@ -69,6 +69,10 @@ pub enum KnownRecord { LexiconsShWeaverNotebookAuthors( Box, ), + #[serde(rename = "sh.weaver.notebook.book")] + LexiconsShWeaverNotebookBook( + Box, + ), #[serde(rename = "sh.weaver.notebook.entry")] LexiconsShWeaverNotebookEntry( Box, @@ -312,6 +316,18 @@ impl From for KnownR KnownRecord::LexiconsShWeaverNotebookAuthors(Box::new(record_data.into())) } } +impl From for KnownRecord { + fn from(record: crate::lexicons::sh::weaver::notebook::book::Record) -> Self { + KnownRecord::LexiconsShWeaverNotebookBook(Box::new(record)) + } +} +impl From for KnownRecord { + fn from( + record_data: crate::lexicons::sh::weaver::notebook::book::RecordData, + ) -> Self { + KnownRecord::LexiconsShWeaverNotebookBook(Box::new(record_data.into())) + } +} impl From for KnownRecord { fn from(record: crate::lexicons::sh::weaver::notebook::entry::Record) -> Self { KnownRecord::LexiconsShWeaverNotebookEntry(Box::new(record)) diff --git a/crates/weaver-common/src/lexicons/sh/weaver/actor/defs.rs b/crates/weaver-common/src/lexicons/sh/weaver/actor/defs.rs index 6af5065..7335bd0 100644 --- a/crates/weaver-common/src/lexicons/sh/weaver/actor/defs.rs +++ b/crates/weaver-common/src/lexicons/sh/weaver/actor/defs.rs @@ -17,5 +17,10 @@ pub struct ProfileViewData { pub indexed_at: core::option::Option, #[serde(skip_serializing_if = "core::option::Option::is_none")] pub labels: core::option::Option>, + #[serde(skip_serializing_if = "core::option::Option::is_none")] + pub links: core::option::Option>, + ///Free-form location text. + #[serde(skip_serializing_if = "core::option::Option::is_none")] + pub location: core::option::Option, } pub type ProfileView = atrium_api::types::Object; diff --git a/crates/weaver-common/src/lexicons/sh/weaver/notebook.rs b/crates/weaver-common/src/lexicons/sh/weaver/notebook.rs index d7aea07..11a5de2 100644 --- a/crates/weaver-common/src/lexicons/sh/weaver/notebook.rs +++ b/crates/weaver-common/src/lexicons/sh/weaver/notebook.rs @@ -1,6 +1,7 @@ // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. //!Definitions for the `sh.weaver.notebook` namespace. pub mod authors; +pub mod book; pub mod defs; pub mod entry; #[derive(Debug)] @@ -10,6 +11,12 @@ impl atrium_api::types::Collection for Authors { type Record = authors::Record; } #[derive(Debug)] +pub struct Book; +impl atrium_api::types::Collection for Book { + const NSID: &'static str = "sh.weaver.notebook.book"; + type Record = book::Record; +} +#[derive(Debug)] pub struct Entry; impl atrium_api::types::Collection for Entry { const NSID: &'static str = "sh.weaver.notebook.entry"; diff --git a/crates/weaver-common/src/lexicons/sh/weaver/notebook/authors.rs b/crates/weaver-common/src/lexicons/sh/weaver/notebook/authors.rs index 10bce29..94a0a5e 100644 --- a/crates/weaver-common/src/lexicons/sh/weaver/notebook/authors.rs +++ b/crates/weaver-common/src/lexicons/sh/weaver/notebook/authors.rs @@ -21,14 +21,18 @@ pub struct AuthorListItemData { #[serde(skip_serializing_if = "core::option::Option::is_none")] pub index: core::option::Option, #[serde(skip_serializing_if = "core::option::Option::is_none")] - pub profile: core::option::Option>, + pub profile: core::option::Option< + atrium_api::types::Union, + >, } pub type AuthorListItem = atrium_api::types::Object; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "$type")] pub enum AuthorListItemProfileRefs { #[serde(rename = "app.bsky.actor.defs#profileViewBasic")] - AppBskyActorDefsProfileViewBasic(Box), - #[serde(rename = "sh.weaver.actor.profile")] - ShWeaverActorProfileMain(Box), + AppBskyActorDefsProfileViewBasic( + Box, + ), + #[serde(rename = "sh.weaver.actor.defs#profileView")] + ShWeaverActorDefsProfileView(Box), } diff --git a/crates/weaver-common/src/lexicons/sh/weaver/notebook/book.rs b/crates/weaver-common/src/lexicons/sh/weaver/notebook/book.rs new file mode 100644 index 0000000..6433281 --- /dev/null +++ b/crates/weaver-common/src/lexicons/sh/weaver/notebook/book.rs @@ -0,0 +1,19 @@ +// @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. +//!Definitions for the `sh.weaver.notebook.book` namespace. +use atrium_api::types::TryFromUnknown; +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct RecordData { + pub authors: crate::sh::weaver::notebook::defs::AuthorListView, + pub cid: atrium_api::types::string::Cid, + #[serde(skip_serializing_if = "core::option::Option::is_none")] + pub created_at: core::option::Option, + pub entry_list: Vec, + pub uri: String, +} +pub type Record = atrium_api::types::Object; +impl From for RecordData { + fn from(value: atrium_api::types::Unknown) -> Self { + Self::try_from_unknown(value).unwrap() + } +} diff --git a/crates/weaver-common/src/lexicons/sh/weaver/notebook/defs.rs b/crates/weaver-common/src/lexicons/sh/weaver/notebook/defs.rs index bda72b4..6070299 100644 --- a/crates/weaver-common/src/lexicons/sh/weaver/notebook/defs.rs +++ b/crates/weaver-common/src/lexicons/sh/weaver/notebook/defs.rs @@ -2,8 +2,44 @@ //!Definitions for the `sh.weaver.notebook.defs` namespace. #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] +pub struct AuthorListViewData { + #[serde(skip_serializing_if = "core::option::Option::is_none")] + pub cid: core::option::Option, + pub index: i64, + #[serde(skip_serializing_if = "core::option::Option::is_none")] + pub record: core::option::Option, + #[serde(skip_serializing_if = "core::option::Option::is_none")] + pub uri: core::option::Option, +} +pub type AuthorListView = atrium_api::types::Object; +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct BookEntryRefData { + pub entry: EntryView, +} +pub type BookEntryRef = atrium_api::types::Object; +///An ordered entry in a Weaver notebook. +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct BookEntryViewData { + pub entry: EntryView, + pub index: i64, + #[serde(skip_serializing_if = "core::option::Option::is_none")] + pub next: core::option::Option, + #[serde(skip_serializing_if = "core::option::Option::is_none")] + pub prev: core::option::Option, +} +pub type BookEntryView = atrium_api::types::Object; +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] pub struct EntryViewData { + #[serde(skip_serializing_if = "core::option::Option::is_none")] + pub authors: core::option::Option, pub cid: atrium_api::types::string::Cid, + pub indexed_at: atrium_api::types::string::Datetime, + pub record: atrium_api::types::Unknown, + #[serde(skip_serializing_if = "core::option::Option::is_none")] + pub rendered_view: core::option::Option, pub uri: String, } pub type EntryView = atrium_api::types::Object; diff --git a/crates/weaver-common/src/lib.rs b/crates/weaver-common/src/lib.rs index 1dd01df..2698680 100644 --- a/crates/weaver-common/src/lib.rs +++ b/crates/weaver-common/src/lib.rs @@ -1,17 +1,16 @@ +pub mod client; +pub mod config; pub mod error; pub mod lexicons; pub mod oauth; +pub mod resolver; pub use lexicons::*; -use atrium_identity::handle::DnsTxtResolver; - pub use crate::error::{Error, IoError, ParseError, SerDeError}; /// Canonical Cow for us, thanks Amos pub use merde::CowStr; -use hickory_resolver::TokioAsyncResolver; - /// too many cows, so we have conversions pub fn mcow_to_cow(cow: CowStr<'_>) -> std::borrow::Cow<'_, str> { match cow { @@ -36,31 +35,3 @@ pub fn mdcow_to_cow(cow: markdown_weaver::CowStr<'_>) -> std::borrow::Cow<'_, st markdown_weaver::CowStr::Inlined(s) => std::borrow::Cow::Owned(s.as_ref().to_owned()), } } - -pub struct HickoryDnsTxtResolver { - resolver: TokioAsyncResolver, -} - -impl Default for HickoryDnsTxtResolver { - fn default() -> Self { - Self { - resolver: TokioAsyncResolver::tokio_from_system_conf() - .expect("failed to create resolver"), - } - } -} - -impl DnsTxtResolver for HickoryDnsTxtResolver { - async fn resolve( - &self, - query: &str, - ) -> core::result::Result, Box> { - Ok(self - .resolver - .txt_lookup(query) - .await? - .iter() - .map(|txt| txt.to_string()) - .collect()) - } -} diff --git a/crates/weaver-common/src/oauth.rs b/crates/weaver-common/src/oauth.rs index e6cdf03..a7c55a7 100644 --- a/crates/weaver-common/src/oauth.rs +++ b/crates/weaver-common/src/oauth.rs @@ -15,7 +15,7 @@ use atrium_oauth::{ use std::sync::Arc; -use crate::HickoryDnsTxtResolver; +use crate::resolver::HickoryDnsTxtResolver; pub fn default_oauth_client( url: impl AsRef, diff --git a/crates/weaver-common/src/resolver.rs b/crates/weaver-common/src/resolver.rs new file mode 100644 index 0000000..2840645 --- /dev/null +++ b/crates/weaver-common/src/resolver.rs @@ -0,0 +1,30 @@ +use atrium_identity::handle::DnsTxtResolver; +use hickory_resolver::TokioAsyncResolver; + +pub struct HickoryDnsTxtResolver { + resolver: TokioAsyncResolver, +} + +impl Default for HickoryDnsTxtResolver { + fn default() -> Self { + Self { + resolver: TokioAsyncResolver::tokio_from_system_conf() + .expect("failed to create resolver"), + } + } +} + +impl DnsTxtResolver for HickoryDnsTxtResolver { + async fn resolve( + &self, + query: &str, + ) -> core::result::Result, Box> { + Ok(self + .resolver + .txt_lookup(query) + .await? + .iter() + .map(|txt| txt.to_string()) + .collect()) + } +} diff --git a/docs/oauth/client-metadata.json b/docs/oauth/client-metadata.json new file mode 100644 index 0000000..38f8efa --- /dev/null +++ b/docs/oauth/client-metadata.json @@ -0,0 +1,12 @@ +{ + "client_id": "https://appview.weaver.sh/oauth/client-metadata.json", + "application_type": "native", + "client_name": "Weaver Appview", + "client_uri": "https://appview.weaver.sh", + "dpop_bound_access_tokens": true, + "grant_types": ["authorization_code", "refresh_token"], + "redirect_uris": ["http://127.0.0.1:8080/oauth/callback"], + "response_types": ["code"], + "scope": "atproto transition:generic", + "token_endpoint_auth_method": "none" +} diff --git a/flake.nix b/flake.nix index 15c420d..89e9b1e 100644 --- a/flake.nix +++ b/flake.nix @@ -256,6 +256,7 @@ weaver-server = flake-utils.lib.mkApp { drv = weaver-server; }; + }; devShells.default = craneLib.devShell { diff --git a/lexicon-codegen.sh b/lexicon-codegen.sh index 1e5dc80..3e5375b 100755 --- a/lexicon-codegen.sh +++ b/lexicon-codegen.sh @@ -2,7 +2,9 @@ #cargo install esquema-cli --locked --git https://github.com/fatfingers23/esquema.git +rm -rf ./target/lexicons mkdir -p ./target/lexicons + cp -r ./lexicons ./target/lexicons cp -r ./atproto/lexicons ./target/lexicons diff --git a/lexicons/sh.weaver/notebook/defs.json b/lexicons/sh.weaver/notebook/defs.json deleted file mode 100644 index 34bb9b5..0000000 --- a/lexicons/sh.weaver/notebook/defs.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "lexicon": 1, - "id": "sh.weaver.notebook.defs", - "defs": { - "entryView": { - "type": "object", - "required": ["uri", "cid", "author", "record", "indexedAt"], - "authors": { - "type": "array", - "items": { - "type": "union", - "refs": ["app.bsky.actor.defs#profileViewBasic"] - } - }, - "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" } - }, - - "record": { "type": "unknown" }, - "renderedView": { - "type": "ref", - "ref": "#renderedView" - }, - "indexedAt": { "type": "string", "format": "datetime" } - }, - - "renderedView": { - "type": "object", - "required": ["html"], - "properties": { - "html": { - "type": "blob", - "accept": ["text/html"], - "maxSize": 1000000 - }, - "css": { - "type": "blob", - "accept": ["text/css"], - "maxSize": 1000000 - } - }, - "description": "View of a rendered and cached notebook entry" - } - } -} diff --git a/lexicons/sh.tangled/actor.profile.json b/lexicons/sh/tangled/actor.profile.json similarity index 100% rename from lexicons/sh.tangled/actor.profile.json rename to lexicons/sh/tangled/actor.profile.json diff --git a/lexicons/sh.weaver/actor/defs.json b/lexicons/sh/weaver/actor/defs.json similarity index 63% rename from lexicons/sh.weaver/actor/defs.json rename to lexicons/sh/weaver/actor/defs.json index 4d5610d..f10663a 100644 --- a/lexicons/sh.weaver/actor/defs.json +++ b/lexicons/sh/weaver/actor/defs.json @@ -19,7 +19,22 @@ "maxLength": 2560 }, "avatar": { "type": "string", "format": "uri" }, - + "location": { + "type": "string", + "description": "Free-form location text.", + "maxGraphemes": 40, + "maxLength": 400 + }, + "links": { + "type": "array", + "minLength": 0, + "maxLength": 5, + "items": { + "type": "string", + "description": "Any URI, intended for social profiles or websites, can be used to link DIDs/AT-URIs too.", + "format": "uri" + } + }, "indexedAt": { "type": "string", "format": "datetime" }, "createdAt": { "type": "string", "format": "datetime" }, diff --git a/lexicons/sh.weaver/actor/profile.json b/lexicons/sh/weaver/actor/profile.json similarity index 100% rename from lexicons/sh.weaver/actor/profile.json rename to lexicons/sh/weaver/actor/profile.json diff --git a/lexicons/sh.weaver/embed/defs.json b/lexicons/sh/weaver/embed/defs.json similarity index 100% rename from lexicons/sh.weaver/embed/defs.json rename to lexicons/sh/weaver/embed/defs.json diff --git a/lexicons/sh.weaver/embed/external.json b/lexicons/sh/weaver/embed/external.json similarity index 100% rename from lexicons/sh.weaver/embed/external.json rename to lexicons/sh/weaver/embed/external.json diff --git a/lexicons/sh.weaver/embed/images.json b/lexicons/sh/weaver/embed/images.json similarity index 100% rename from lexicons/sh.weaver/embed/images.json rename to lexicons/sh/weaver/embed/images.json diff --git a/lexicons/sh.weaver/embed/recordWithMedia.json b/lexicons/sh/weaver/embed/recordWithMedia.json similarity index 100% rename from lexicons/sh.weaver/embed/recordWithMedia.json rename to lexicons/sh/weaver/embed/recordWithMedia.json diff --git a/lexicons/sh.weaver/embed/records.json b/lexicons/sh/weaver/embed/records.json similarity index 100% rename from lexicons/sh.weaver/embed/records.json rename to lexicons/sh/weaver/embed/records.json diff --git a/lexicons/sh.weaver/embed/video.json b/lexicons/sh/weaver/embed/video.json similarity index 100% rename from lexicons/sh.weaver/embed/video.json rename to lexicons/sh/weaver/embed/video.json diff --git a/lexicons/sh.weaver/notebook/authors.json b/lexicons/sh/weaver/notebook/authors.json similarity index 88% rename from lexicons/sh.weaver/notebook/authors.json rename to lexicons/sh/weaver/notebook/authors.json index 6795001..e054842 100644 --- a/lexicons/sh.weaver/notebook/authors.json +++ b/lexicons/sh/weaver/notebook/authors.json @@ -28,7 +28,7 @@ "properties": { "profile": { "type": "union", - "refs": ["app.bsky.actor.defs#profileViewBasic", "sh.weaver.actor.profile", "sh.weaver.actor.profile"] + "refs": ["app.bsky.actor.defs#profileViewBasic", "sh.weaver.actor.defs#profileView"] }, "index": { "type": "integer" } } diff --git a/lexicons/sh/weaver/notebook/book.json b/lexicons/sh/weaver/notebook/book.json new file mode 100644 index 0000000..7fd541e --- /dev/null +++ b/lexicons/sh/weaver/notebook/book.json @@ -0,0 +1,31 @@ +{ + "lexicon": 1, + "id": "sh.weaver.notebook.book", + "defs": { + "main": { + "type": "record", + "description": "Authors of a Weaver notebook.", + "key": "tid", + "record": { + "type": "object", + "required": ["cid", "uri", "authors", "entryList"], + "properties": { + "uri": { "type": "string", "format": "at-uri" }, + "cid": { "type": "string", "format": "cid" }, + "authors": { + "type": "ref", + "ref": "sh.weaver.notebook.defs#authorListView" + }, + "entryList": { + "type": "array", + "items": { + "type": "ref", + "ref": "sh.weaver.notebook.defs#bookEntryView" + } + }, + "createdAt": { "type": "string", "format": "datetime" } + } + } + } + } +} diff --git a/lexicons/sh/weaver/notebook/defs.json b/lexicons/sh/weaver/notebook/defs.json new file mode 100644 index 0000000..3b9c9e0 --- /dev/null +++ b/lexicons/sh/weaver/notebook/defs.json @@ -0,0 +1,84 @@ +{ + "lexicon": 1, + "id": "sh.weaver.notebook.defs", + "defs": { + "entryView": { + "type": "object", + "required": ["uri", "cid", "author", "record", "indexedAt"], + + "properties": { + "uri": { "type": "string", "format": "at-uri" }, + "cid": { "type": "string", "format": "cid" }, + "authors": { + "type": "ref", + "ref": "#authorListView" + }, + "record": { "type": "unknown" }, + "renderedView": { + "type": "ref", + "ref": "#renderedView" + }, + "indexedAt": { "type": "string", "format": "datetime" } + } + }, + + "renderedView": { + "type": "object", + "required": ["html"], + "properties": { + "html": { + "type": "blob", + "accept": ["text/html"], + "maxSize": 1000000 + }, + "css": { + "type": "blob", + "accept": ["text/css"], + "maxSize": 1000000 + } + }, + "description": "View of a rendered and cached notebook entry" + }, + "authorListView": { + "type": "object", + "required": ["profile", "index"], + "properties": { + "uri": { "type": "string", "format": "at-uri" }, + "cid": { "type": "string", "format": "cid" }, + "record": { "type": "unknown" }, + "index": { "type": "integer" } + } + }, + + "bookEntryView": { + "type": "object", + "description": "An ordered entry in a Weaver notebook.", + "required": ["entry", "index"], + "properties": { + "entry": { + "type": "ref", + "ref": "#entryView" + }, + "index": { "type": "integer" }, + "next": { + "type": "ref", + "ref": "#bookEntryRef" + }, + "prev": { + "type": "ref", + "ref": "#bookEntryRef" + } + } + }, + "bookEntryRef": { + "type": "object", + "required": ["entry"], + "properties": { + "entry": { + "type": "ref", + "ref": "#entryView" + } + } + } + } +} diff --git a/lexicons/sh.weaver/notebook/entry.json b/lexicons/sh/weaver/notebook/entry.json similarity index 100% rename from lexicons/sh.weaver/notebook/entry.json rename to lexicons/sh/weaver/notebook/entry.json