bit of renderer work, docs pages deploy

+1216 -383
+46
.github/workflows/static.yml
··· 1 + # Simple workflow for deploying static content to GitHub Pages 2 + name: Deploy static content to Pages 3 + 4 + on: 5 + # Runs on pushes targeting the default branch 6 + push: 7 + branches: ["main"] 8 + 9 + # Allows you to run this workflow manually from the Actions tab 10 + workflow_dispatch: 11 + 12 + # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 + permissions: 14 + contents: read 15 + pages: write 16 + id-token: write 17 + 18 + # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 + # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 + concurrency: 21 + group: "pages" 22 + cancel-in-progress: false 23 + 24 + jobs: 25 + # Single deploy job since we're just deploying 26 + deploy: 27 + environment: 28 + name: github-pages 29 + url: ${{ steps.deployment.outputs.page_url }} 30 + 31 + runs-on: ubuntu-latest 32 + steps: 33 + - name: Checkout 34 + uses: actions/checkout@v4 35 + with: 36 + submodules: recursive 37 + - name: Setup Pages 38 + uses: actions/configure-pages@v5 39 + - name: Upload artifact 40 + uses: actions/upload-pages-artifact@v3 41 + with: 42 + # Upload entire repository 43 + path: "./docs" 44 + - name: Deploy to GitHub Pages 45 + id: deployment 46 + uses: actions/deploy-pages@v4
+3 -1
.gitignore
··· 1 1 /target 2 - result* 2 + /result 3 + /result-lib 4 + /dist 3 5 /node_modules 4 6 5 7 .direnv
+115 -209
Cargo.lock
··· 306 306 307 307 [[package]] 308 308 name = "axum" 309 - version = "0.7.9" 310 - source = "registry+https://github.com/rust-lang/crates.io-index" 311 - checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" 312 - dependencies = [ 313 - "async-trait", 314 - "axum-core 0.4.5", 315 - "bytes", 316 - "futures-util", 317 - "http", 318 - "http-body", 319 - "http-body-util", 320 - "hyper", 321 - "hyper-util", 322 - "itoa", 323 - "matchit 0.7.3", 324 - "memchr", 325 - "mime", 326 - "percent-encoding", 327 - "pin-project-lite", 328 - "rustversion", 329 - "serde", 330 - "serde_json", 331 - "serde_path_to_error", 332 - "serde_urlencoded", 333 - "sync_wrapper", 334 - "tokio", 335 - "tower", 336 - "tower-layer", 337 - "tower-service", 338 - "tracing", 339 - ] 340 - 341 - [[package]] 342 - name = "axum" 343 309 version = "0.8.4" 344 310 source = "registry+https://github.com/rust-lang/crates.io-index" 345 311 checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" 346 312 dependencies = [ 347 - "axum-core 0.5.2", 313 + "axum-core", 348 314 "bytes", 349 315 "form_urlencoded", 350 316 "futures-util", ··· 354 320 "hyper", 355 321 "hyper-util", 356 322 "itoa", 357 - "matchit 0.8.4", 323 + "matchit", 358 324 "memchr", 359 325 "mime", 360 326 "percent-encoding", ··· 367 333 "sync_wrapper", 368 334 "tokio", 369 335 "tower", 370 - "tower-layer", 371 - "tower-service", 372 - "tracing", 373 - ] 374 - 375 - [[package]] 376 - name = "axum-core" 377 - version = "0.4.5" 378 - source = "registry+https://github.com/rust-lang/crates.io-index" 379 - checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" 380 - dependencies = [ 381 - "async-trait", 382 - "bytes", 383 - "futures-util", 384 - "http", 385 - "http-body", 386 - "http-body-util", 387 - "mime", 388 - "pin-project-lite", 389 - "rustversion", 390 - "sync_wrapper", 391 336 "tower-layer", 392 337 "tower-service", 393 338 "tracing", ··· 1266 1211 ] 1267 1212 1268 1213 [[package]] 1269 - name = "facet" 1270 - version = "0.9.8" 1271 - source = "registry+https://github.com/rust-lang/crates.io-index" 1272 - checksum = "36b55e98a6cc63c843db91f6db4be9f47067b9bd07d93560da0ba4c0f55f20fd" 1273 - dependencies = [ 1274 - "facet-core", 1275 - "facet-derive", 1276 - ] 1277 - 1278 - [[package]] 1279 - name = "facet-core" 1280 - version = "0.12.0" 1281 - source = "registry+https://github.com/rust-lang/crates.io-index" 1282 - checksum = "5227e1c925a1f21ea3ebff90e08af0b34b59975171cf00fcee36b917941d1ac0" 1283 - dependencies = [ 1284 - "bitflags 2.9.0", 1285 - "camino", 1286 - "impls", 1287 - ] 1288 - 1289 - [[package]] 1290 - name = "facet-derive" 1291 - version = "0.9.8" 1292 - source = "registry+https://github.com/rust-lang/crates.io-index" 1293 - checksum = "9982f286c2121f36d6921c7ecedde49158a5ba8eef622a4fd777ff502c1348e3" 1294 - dependencies = [ 1295 - "facet-core", 1296 - "facet-derive-emit", 1297 - ] 1298 - 1299 - [[package]] 1300 - name = "facet-derive-emit" 1301 - version = "0.15.0" 1302 - source = "registry+https://github.com/rust-lang/crates.io-index" 1303 - checksum = "2ed20e29f8aa502cf1263eb040e3442ef84636e89a88ee8dffcda336a1152a38" 1304 - dependencies = [ 1305 - "facet-derive-parse", 1306 - ] 1307 - 1308 - [[package]] 1309 - name = "facet-derive-parse" 1310 - version = "0.15.0" 1311 - source = "registry+https://github.com/rust-lang/crates.io-index" 1312 - checksum = "71ea122ff95ac65c86bf18d0ba7f04f087ef5dd20f7dc76cb5bd69cbb14bd8ad" 1313 - dependencies = [ 1314 - "unsynn", 1315 - ] 1316 - 1317 - [[package]] 1318 1214 name = "fallible-iterator" 1319 1215 version = "0.2.0" 1320 1216 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1420 1316 checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 1421 1317 dependencies = [ 1422 1318 "percent-encoding", 1319 + ] 1320 + 1321 + [[package]] 1322 + name = "fsevent-sys" 1323 + version = "4.1.0" 1324 + source = "registry+https://github.com/rust-lang/crates.io-index" 1325 + checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" 1326 + dependencies = [ 1327 + "libc", 1423 1328 ] 1424 1329 1425 1330 [[package]] ··· 2007 1912 ] 2008 1913 2009 1914 [[package]] 2010 - name = "impls" 2011 - version = "1.0.3" 2012 - source = "registry+https://github.com/rust-lang/crates.io-index" 2013 - checksum = "7a46645bbd70538861a90d0f26c31537cdf1e44aae99a794fb75a664b70951bc" 2014 - 2015 - [[package]] 2016 1915 name = "indexmap" 2017 1916 version = "1.9.3" 2018 1917 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2031 1930 dependencies = [ 2032 1931 "equivalent", 2033 1932 "hashbrown 0.15.3", 1933 + ] 1934 + 1935 + [[package]] 1936 + name = "inotify" 1937 + version = "0.11.0" 1938 + source = "registry+https://github.com/rust-lang/crates.io-index" 1939 + checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" 1940 + dependencies = [ 1941 + "bitflags 2.9.0", 1942 + "inotify-sys", 1943 + "libc", 1944 + ] 1945 + 1946 + [[package]] 1947 + name = "inotify-sys" 1948 + version = "0.1.5" 1949 + source = "registry+https://github.com/rust-lang/crates.io-index" 1950 + checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" 1951 + dependencies = [ 1952 + "libc", 2034 1953 ] 2035 1954 2036 1955 [[package]] ··· 2181 2100 ] 2182 2101 2183 2102 [[package]] 2184 - name = "kdl" 2185 - version = "6.3.4" 2103 + name = "kqueue" 2104 + version = "1.1.1" 2186 2105 source = "registry+https://github.com/rust-lang/crates.io-index" 2187 - checksum = "12661358400b02cbbf1fbd05f0a483335490e8a6bd1867620f2eeb78f304a22f" 2106 + checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" 2188 2107 dependencies = [ 2189 - "miette", 2190 - "num", 2191 - "thiserror 1.0.69", 2192 - "winnow 0.6.24", 2108 + "kqueue-sys", 2109 + "libc", 2110 + ] 2111 + 2112 + [[package]] 2113 + name = "kqueue-sys" 2114 + version = "1.0.4" 2115 + source = "registry+https://github.com/rust-lang/crates.io-index" 2116 + checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" 2117 + dependencies = [ 2118 + "bitflags 1.3.2", 2119 + "libc", 2193 2120 ] 2194 2121 2195 2122 [[package]] ··· 2365 2292 dependencies = [ 2366 2293 "regex-automata 0.1.10", 2367 2294 ] 2368 - 2369 - [[package]] 2370 - name = "matchit" 2371 - version = "0.7.3" 2372 - source = "registry+https://github.com/rust-lang/crates.io-index" 2373 - checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" 2374 2295 2375 2296 [[package]] 2376 2297 name = "matchit" ··· 2565 2486 checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 2566 2487 dependencies = [ 2567 2488 "libc", 2489 + "log", 2568 2490 "wasi 0.11.0+wasi-snapshot-preview1", 2569 2491 "windows-sys 0.52.0", 2570 2492 ] ··· 2632 2554 ] 2633 2555 2634 2556 [[package]] 2635 - name = "mutants" 2636 - version = "0.0.3" 2637 - source = "registry+https://github.com/rust-lang/crates.io-index" 2638 - checksum = "bc0287524726960e07b119cebd01678f852f147742ae0d925e6a520dca956126" 2639 - 2640 - [[package]] 2641 2557 name = "n0-future" 2642 2558 version = "0.1.3" 2643 2559 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2676 2592 ] 2677 2593 2678 2594 [[package]] 2679 - name = "nu-ansi-term" 2680 - version = "0.46.0" 2595 + name = "notify" 2596 + version = "8.0.0" 2681 2597 source = "registry+https://github.com/rust-lang/crates.io-index" 2682 - checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 2598 + checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" 2683 2599 dependencies = [ 2684 - "overload", 2685 - "winapi", 2600 + "bitflags 2.9.0", 2601 + "filetime", 2602 + "fsevent-sys", 2603 + "inotify", 2604 + "kqueue", 2605 + "libc", 2606 + "log", 2607 + "mio", 2608 + "notify-types", 2609 + "walkdir", 2610 + "windows-sys 0.59.0", 2686 2611 ] 2687 2612 2688 2613 [[package]] 2689 - name = "num" 2690 - version = "0.4.3" 2614 + name = "notify-types" 2615 + version = "2.0.0" 2691 2616 source = "registry+https://github.com/rust-lang/crates.io-index" 2692 - checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" 2693 - dependencies = [ 2694 - "num-bigint", 2695 - "num-complex", 2696 - "num-integer", 2697 - "num-iter", 2698 - "num-rational", 2699 - "num-traits", 2700 - ] 2617 + checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" 2701 2618 2702 2619 [[package]] 2703 - name = "num-bigint" 2704 - version = "0.4.6" 2620 + name = "nu-ansi-term" 2621 + version = "0.46.0" 2705 2622 source = "registry+https://github.com/rust-lang/crates.io-index" 2706 - checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 2623 + checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 2707 2624 dependencies = [ 2708 - "num-integer", 2709 - "num-traits", 2625 + "overload", 2626 + "winapi", 2710 2627 ] 2711 2628 2712 2629 [[package]] ··· 2727 2644 ] 2728 2645 2729 2646 [[package]] 2730 - name = "num-complex" 2731 - version = "0.4.6" 2732 - source = "registry+https://github.com/rust-lang/crates.io-index" 2733 - checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" 2734 - dependencies = [ 2735 - "num-traits", 2736 - ] 2737 - 2738 - [[package]] 2739 2647 name = "num-conv" 2740 2648 version = "0.1.0" 2741 2649 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2757 2665 checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 2758 2666 dependencies = [ 2759 2667 "autocfg", 2760 - "num-integer", 2761 - "num-traits", 2762 - ] 2763 - 2764 - [[package]] 2765 - name = "num-rational" 2766 - version = "0.4.2" 2767 - source = "registry+https://github.com/rust-lang/crates.io-index" 2768 - checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" 2769 - dependencies = [ 2770 - "num-bigint", 2771 2668 "num-integer", 2772 2669 "num-traits", 2773 2670 ] ··· 3963 3860 ] 3964 3861 3965 3862 [[package]] 3966 - name = "shadow_counted" 3967 - version = "0.4.0" 3968 - source = "registry+https://github.com/rust-lang/crates.io-index" 3969 - checksum = "65da48d447333cebe1aadbdd3662f3ba56e76e67f53bc46f3dd5f67c74629d6b" 3970 - 3971 - [[package]] 3972 3863 name = "sharded-slab" 3973 3864 version = "0.1.7" 3974 3865 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4479 4370 "serde_spanned", 4480 4371 "toml_datetime", 4481 4372 "toml_write", 4482 - "winnow 0.7.10", 4373 + "winnow", 4483 4374 ] 4484 4375 4485 4376 [[package]] ··· 4510 4401 source = "registry+https://github.com/rust-lang/crates.io-index" 4511 4402 checksum = "151b5a3e3c45df17466454bb74e9ecedecc955269bdedbf4d150dfa393b55a36" 4512 4403 dependencies = [ 4513 - "axum-core 0.5.2", 4404 + "axum-core", 4514 4405 "cookie", 4515 4406 "futures-util", 4516 4407 "http", ··· 4742 4633 checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" 4743 4634 4744 4635 [[package]] 4745 - name = "unsynn" 4746 - version = "0.0.26" 4747 - source = "registry+https://github.com/rust-lang/crates.io-index" 4748 - checksum = "14013db4ac11f92d4df4b56edb431eab9c1d9f7aee4791f3205b6a1119b83f54" 4749 - dependencies = [ 4750 - "mutants", 4751 - "proc-macro2", 4752 - "shadow_counted", 4753 - ] 4754 - 4755 - [[package]] 4756 4636 name = "untrusted" 4757 4637 version = "0.7.1" 4758 4638 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4935 4815 "atrium-identity", 4936 4816 "atrium-oauth", 4937 4817 "atrium-xrpc", 4938 - "axum 0.8.4", 4818 + "axum", 4939 4819 "chrono", 4940 4820 "clap", 4941 4821 "dashmap", ··· 4943 4823 "diesel-async", 4944 4824 "diesel_migrations", 4945 4825 "dotenvy", 4946 - "facet", 4947 4826 "hyper", 4948 4827 "jose", 4949 4828 "jose-jwk", 4950 - "kdl", 4951 4829 "merde", 4952 4830 "miette", 4953 4831 "minijinja", ··· 4998 4876 "atrium-api", 4999 4877 "atrium-common", 5000 4878 "atrium-identity", 5001 - "atrium-lex", 5002 4879 "atrium-oauth", 5003 4880 "atrium-xrpc", 5004 - "atrium-xrpc-client", 5005 - "compact_string", 5006 4881 "esquema-codegen", 5007 4882 "hickory-resolver", 5008 4883 "http", 5009 - "jose", 5010 4884 "jose-jwk", 5011 4885 "libsqlite3-sys", 5012 4886 "markdown-weaver", 5013 4887 "merde", 5014 4888 "miette", 5015 4889 "minijinja", 4890 + "multibase", 5016 4891 "n0-future", 5017 4892 "owo-colors", 5018 4893 "reqwest", ··· 5025 4900 "thiserror 2.0.12", 5026 4901 "tokio", 5027 4902 "toml", 4903 + "tower-layer", 4904 + "tower-service", 5028 4905 "tracing", 5029 4906 "weaver-workspace-hack", 5030 4907 ] ··· 5033 4910 name = "weaver-renderer" 5034 4911 version = "0.1.0" 5035 4912 dependencies = [ 4913 + "atrium-api", 4914 + "compact_string", 5036 4915 "n0-future", 5037 4916 "weaver-common", 5038 4917 "weaver-workspace-hack", ··· 5042 4921 name = "weaver-server" 5043 4922 version = "0.1.0" 5044 4923 dependencies = [ 5045 - "axum 0.7.9", 4924 + "axum", 5046 4925 "clap", 4926 + "notify", 5047 4927 "tokio", 5048 4928 "weaver-common", 5049 4929 "weaver-workspace-hack", ··· 5053 4933 name = "weaver-workspace-hack" 5054 4934 version = "0.1.0" 5055 4935 dependencies = [ 4936 + "base16ct", 4937 + "byteorder", 4938 + "chrono", 5056 4939 "data-encoding", 4940 + "der", 4941 + "diesel", 4942 + "diesel-async", 4943 + "diesel_derives", 4944 + "ecdsa", 4945 + "either", 4946 + "elliptic-curve", 4947 + "fastrand", 4948 + "ff", 5057 4949 "futures-channel", 5058 - "futures-core", 4950 + "generic-array", 4951 + "group", 4952 + "hashbrown 0.15.3", 5059 4953 "idna", 4954 + "libsqlite3-sys", 4955 + "log", 5060 4956 "memchr", 4957 + "miette", 4958 + "mime_guess", 4959 + "minijinja", 4960 + "num-traits", 4961 + "p384", 5061 4962 "percent-encoding", 4963 + "pkcs8", 5062 4964 "proc-macro2", 5063 4965 "quote", 4966 + "rand_core 0.6.4", 4967 + "reqwest", 5064 4968 "scopeguard", 4969 + "sec1", 4970 + "serde", 4971 + "serde_json", 4972 + "sha2", 5065 4973 "smallvec", 4974 + "spki", 4975 + "subtle", 5066 4976 "syn", 4977 + "time", 5067 4978 "tokio", 4979 + "tokio-util", 4980 + "toml", 5068 4981 "tracing", 4982 + "tracing-core", 4983 + "zeroize", 5069 4984 ] 5070 4985 5071 4986 [[package]] ··· 5474 5389 version = "0.53.0" 5475 5390 source = "registry+https://github.com/rust-lang/crates.io-index" 5476 5391 checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 5477 - 5478 - [[package]] 5479 - name = "winnow" 5480 - version = "0.6.24" 5481 - source = "registry+https://github.com/rust-lang/crates.io-index" 5482 - checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" 5483 - dependencies = [ 5484 - "memchr", 5485 - ] 5486 5392 5487 5393 [[package]] 5488 5394 name = "winnow"
-1
Cargo.toml
··· 24 24 25 25 serde = { version = "1.0", features = ["derive"] } 26 26 merde = { version = "10.0.6", features = ["full"] } 27 - facet = { version = "0.9.6", features = ["camino"] } 28 27 29 28 minijinja = { version = "2.9.0", default-features = false } 30 29 minijinja-contrib = { version = "2.9.0", default-features = false }
-2
crates/weaver-appview/Cargo.toml
··· 56 56 diesel = { version = "2.2.0", features = ["postgres", "serde_json","chrono"] } 57 57 diesel-async = { version = "0.5.2", features = ["postgres", "deadpool"] } 58 58 diesel_migrations = "2.2.0" 59 - facet = { workspace = true } 60 59 61 - kdl = "6.3.4" 62 60 toml = { version = "0.8.22", features = ["preserve_order"] } 63 61 jose = { version = "0.0.2", features = ["crypto-rustcrypto"] } 64 62 jose-jwk = "0.1.2"
+1 -1
crates/weaver-appview/src/oauth.rs
··· 10 10 use miette::IntoDiagnostic; 11 11 use miette::Result; 12 12 use miette::miette; 13 - use weaver_common::client::WeaverHttpClient; 13 + use weaver_common::agent::WeaverHttpClient; 14 14 use weaver_common::oauth::WeaverOAuthClient; 15 15 use weaver_common::resolver::HickoryDnsTxtResolver; 16 16
+26 -17
crates/weaver-cli/src/main.rs
··· 6 6 use rouille::Server; 7 7 use std::error; 8 8 use tokio::sync::mpsc; 9 + use weaver_common::agent::WeaverAgent; 9 10 10 11 #[tokio::main] 11 12 async fn main() -> Result<(), Box<dyn error::Error>> { ··· 34 35 35 36 let params = rx.recv().await.unwrap(); 36 37 let (session, _) = client.callback(params).await?; 37 - server_stop.send(()).expect("Failed to stop callbackserver"); 38 - let agent = Agent::new(session); 38 + server_stop 39 + .send(()) 40 + .expect("Failed to stop callback server"); 41 + let agent = WeaverAgent::new(session); 39 42 let output = agent 40 - .api 41 - .app 42 - .bsky 43 - .feed 44 - .get_timeline( 45 - atrium_api::app::bsky::feed::get_timeline::ParametersData { 46 - algorithm: None, 47 - cursor: None, 48 - limit: 3.try_into().ok(), 49 - } 50 - .into(), 51 - ) 43 + .get_profile_pds(agent.did().await.unwrap().into()) 52 44 .await?; 53 - for feed in &output.feed { 54 - println!("{feed:?}"); 55 - } 45 + println!("{output:?}"); 46 + 47 + // let agent = Agent::new(session); 48 + // let output = agent 49 + // .api 50 + // .app 51 + // .bsky 52 + // .feed 53 + // .get_timeline( 54 + // weaver_common::app::bsky::feed::get_timeline::ParametersData { 55 + // algorithm: None, 56 + // cursor: None, 57 + // limit: 3.try_into().ok(), 58 + // } 59 + // .into(), 60 + // ) 61 + // .await?; 62 + // for feed in &output.feed { 63 + // println!("{feed:?}"); 64 + // } 56 65 server_handle.join().unwrap(); 57 66 Ok(()) 58 67 }
+6 -4
crates/weaver-common/Cargo.toml
··· 22 22 atrium-common = "0.1.1" 23 23 atrium-identity = "0.1.3" 24 24 atrium-oauth = "0.1.1" 25 - atrium-xrpc-client = "0.5.13" 26 - atrium-lex = { workspace = true } 25 + 27 26 28 27 merde = { workspace = true, features = ["yaml", "serde"] } 29 28 serde = { workspace = true } ··· 51 50 tracing = { workspace = true } 52 51 hickory-resolver = "0.24.1" 53 52 toml = "0.8.22" 54 - compact_string = "0.1.0" 55 - jose = { version = "0.0.2", features = ["crypto-rustcrypto"] } 53 + #jose = { version = "0.0.2", features = ["crypto-rustcrypto"] } 56 54 jose-jwk = "0.1.2" 57 55 reqwest = "0.12.15" 58 56 57 + 58 + tower-service = "0.3.3" 59 + tower-layer = "0.3.3" 60 + multibase = "0.9.1" 59 61 60 62 61 63
+471
crates/weaver-common/src/agent.rs
··· 1 + use crate::app::bsky::actor::defs::ProfileViewDetailedData; 2 + use crate::error::GenericXrpcError; 3 + use crate::resolver::HickoryDnsTxtResolver; 4 + use crate::sh::weaver::actor::defs::ProfileDataViewInnerRefs; 5 + use atrium_api::agent::{CloneWithProxy, Configure}; 6 + use atrium_api::types::string::{Cid, Did, Handle, Nsid, RecordKey}; 7 + use atrium_api::types::{Collection, Union, Unknown}; 8 + use atrium_api::{agent::SessionManager, types::string::AtIdentifier}; 9 + use atrium_common::resolver::Resolver; 10 + use atrium_identity::did::{CommonDidResolver, CommonDidResolverConfig, DEFAULT_PLC_DIRECTORY_URL}; 11 + use atrium_identity::handle::{AtprotoHandleResolver, AtprotoHandleResolverConfig}; 12 + use atrium_identity::identity_resolver::IdentityResolver; 13 + use atrium_xrpc::{Error as XrpcError, HttpClient, OutputDataOrBytes, XrpcClient, XrpcRequest}; 14 + use http::{Request, Response}; 15 + use serde::{Serialize, de::DeserializeOwned}; 16 + use std::{fmt::Debug, ops::Deref, sync::Arc}; 17 + 18 + use crate::Error; 19 + use crate::client::Service; 20 + 21 + pub struct WeaverHttpClient { 22 + pub client: reqwest::Client, 23 + } 24 + 25 + impl atrium_xrpc::HttpClient for WeaverHttpClient { 26 + async fn send_http( 27 + &self, 28 + request: atrium_xrpc::http::Request<Vec<u8>>, 29 + ) -> core::result::Result< 30 + atrium_xrpc::http::Response<Vec<u8>>, 31 + Box<dyn std::error::Error + Send + Sync + 'static>, 32 + > { 33 + let response = self.client.execute(request.try_into()?).await?; 34 + let mut builder = atrium_xrpc::http::Response::builder().status(response.status()); 35 + for (k, v) in response.headers() { 36 + builder = builder.header(k, v); 37 + } 38 + builder 39 + .body(response.bytes().await?.to_vec()) 40 + .map_err(Into::into) 41 + } 42 + } 43 + 44 + impl Default for WeaverHttpClient { 45 + fn default() -> Self { 46 + Self { 47 + client: reqwest::Client::new(), 48 + } 49 + } 50 + } 51 + 52 + pub struct WeaverAgent<M> 53 + where 54 + M: SessionManager + Send + Sync, 55 + { 56 + session_manager: Arc<Wrapper<M>>, 57 + resolver: Arc< 58 + IdentityResolver< 59 + CommonDidResolver<WeaverHttpClient>, 60 + AtprotoHandleResolver<HickoryDnsTxtResolver, WeaverHttpClient>, 61 + >, 62 + >, 63 + pub api: Service<Wrapper<M>>, 64 + } 65 + 66 + impl<M> WeaverAgent<M> 67 + where 68 + M: SessionManager + Send + Sync, 69 + { 70 + /// Creates a new agent with the given session manager. 71 + 72 + pub fn new(session_manager: M) -> Self { 73 + let session_manager = Arc::new(Wrapper::new(session_manager)); 74 + let api = Service::new(session_manager.clone()); 75 + let http_client = Arc::new(WeaverHttpClient::default()); 76 + let resolver_config = atrium_identity::identity_resolver::IdentityResolverConfig { 77 + did_resolver: CommonDidResolver::new(CommonDidResolverConfig { 78 + plc_directory_url: DEFAULT_PLC_DIRECTORY_URL.to_string(), 79 + http_client: Arc::clone(&http_client), 80 + }), 81 + handle_resolver: AtprotoHandleResolver::new(AtprotoHandleResolverConfig { 82 + dns_txt_resolver: HickoryDnsTxtResolver::default(), 83 + http_client: Arc::clone(&http_client), 84 + }), 85 + }; 86 + Self { 87 + session_manager, 88 + resolver: Arc::new(IdentityResolver::new(resolver_config)), 89 + api, 90 + } 91 + } 92 + 93 + /// Returns the DID of the current session. 94 + 95 + pub async fn did(&self) -> Option<Did> { 96 + self.session_manager.did().await 97 + } 98 + } 99 + 100 + impl<M> WeaverAgent<M> 101 + where 102 + M: SessionManager + Configure + Send + Sync, 103 + { 104 + pub async fn get_bsky_profile( 105 + &self, 106 + actor: AtIdentifier, 107 + ) -> Result<ProfileViewDetailedData, Error> { 108 + use crate::app::bsky::actor::get_profile::*; 109 + let result = self 110 + .api 111 + .app 112 + .bsky 113 + .actor 114 + .get_profile(ParametersData { actor }.into()) 115 + .await?; 116 + Ok(result.data) 117 + } 118 + 119 + pub async fn get_weaver_profile_pds( 120 + &self, 121 + actor: AtIdentifier, 122 + ) -> Result<crate::sh::weaver::actor::defs::ProfileViewData, Error> { 123 + use crate::sh::weaver::actor::*; 124 + let identity = self 125 + .resolver 126 + .resolve(actor.as_ref()) 127 + .await 128 + .expect("valid identifier"); 129 + let did = Did::new(identity.did).expect("valid did"); 130 + let record = self 131 + .get_record( 132 + Nsid::new(Profile::NSID.into()).expect("valid nsid"), 133 + RecordKey::new(Profile::NSID.to_string()).expect("valid key"), 134 + AtIdentifier::Did(did.clone()), 135 + None, 136 + ) 137 + .await?; 138 + let profile_record = profile::RecordData::from(record.value); 139 + 140 + Ok(defs::ProfileViewData { 141 + avatar: profile_record 142 + .avatar 143 + .map(|avatar| crate::avatar_cdn_url(&did, &avatar)), 144 + created_at: profile_record.created_at, 145 + did, 146 + display_name: profile_record.display_name, 147 + handle: Handle::new(actor.as_ref().to_string()).expect("valid handle"), 148 + labels: None, 149 + links: profile_record.links, 150 + location: profile_record.location, 151 + description: profile_record.description, 152 + pronouns: profile_record.pronouns, 153 + indexed_at: None, 154 + }) 155 + } 156 + 157 + pub async fn get_profile_pds( 158 + &self, 159 + actor: AtIdentifier, 160 + ) -> Result<crate::sh::weaver::actor::defs::ProfileDataViewInnerRefs, Error> { 161 + use crate::sh::weaver::actor::defs::ProfileDataViewInnerRefs::*; 162 + let maybe_profile = self.get_weaver_profile_pds(actor.clone()).await; 163 + match maybe_profile { 164 + Ok(profile) => Ok(ShWeaverActorDefsProfileView(Box::new(profile.into()))), 165 + Err(e1) => { 166 + let maybe_profile = self.get_bsky_profile(actor.clone()).await; 167 + match maybe_profile { 168 + Ok(profile) => Ok(AppBskyActorDefsProfileViewDetailed(Box::new( 169 + profile.into(), 170 + ))), 171 + Err(e2) => Err(e2.with_errors(e1)), 172 + } 173 + } 174 + } 175 + } 176 + 177 + pub async fn put_record( 178 + &self, 179 + collection: Nsid, 180 + record: Unknown, 181 + repo: AtIdentifier, 182 + rkey: RecordKey, 183 + ) -> Result<crate::com::atproto::repo::put_record::OutputData, Error> { 184 + use crate::com::atproto::repo::put_record::*; 185 + let result = self 186 + .api 187 + .com 188 + .atproto 189 + .repo 190 + .put_record( 191 + InputData { 192 + collection, 193 + record, 194 + repo, 195 + rkey, 196 + swap_commit: None, 197 + swap_record: None, 198 + validate: None, 199 + } 200 + .into(), 201 + ) 202 + .await?; 203 + Ok(result.data) 204 + } 205 + 206 + pub async fn get_record( 207 + &self, 208 + collection: Nsid, 209 + rkey: RecordKey, 210 + repo: AtIdentifier, 211 + cid: Option<Cid>, 212 + ) -> Result<crate::com::atproto::repo::get_record::OutputData, Error> { 213 + use crate::com::atproto::repo::get_record::*; 214 + let result = self 215 + .api 216 + .com 217 + .atproto 218 + .repo 219 + .get_record( 220 + ParametersData { 221 + collection, 222 + rkey, 223 + repo, 224 + cid, 225 + } 226 + .into(), 227 + ) 228 + .await?; 229 + Ok(result.data) 230 + } 231 + 232 + pub async fn get_blob(&self, did: Did, cid: Cid) -> Result<Vec<u8>, Error> { 233 + use crate::com::atproto::sync::get_blob::*; 234 + let result = self 235 + .api 236 + .com 237 + .atproto 238 + .sync 239 + .get_blob(ParametersData { did, cid }.into()) 240 + .await?; 241 + Ok(result) 242 + } 243 + 244 + pub async fn upload_blob( 245 + &self, 246 + input: Vec<u8>, 247 + ) -> Result<crate::com::atproto::repo::upload_blob::OutputData, Error> { 248 + let result = self.api.com.atproto.repo.upload_blob(input.into()).await?; 249 + Ok(result.data) 250 + } 251 + 252 + pub async fn upload_artifact( 253 + &self, 254 + content: String, 255 + mime_type: Option<String>, 256 + ) -> Result<crate::com::atproto::repo::upload_blob::OutputData, Error> { 257 + let encoding = if let Some(mime_type) = mime_type { 258 + Some(mime_type) 259 + } else { 260 + Some(String::from("*/*")) 261 + }; 262 + let input = content.into_bytes(); 263 + let response = self 264 + .session_manager 265 + .inner 266 + .send_xrpc::<(), Vec<u8>, crate::com::atproto::repo::upload_blob::OutputData, crate::com::atproto::repo::upload_blob::Error>( 267 + &atrium_xrpc::XrpcRequest { 268 + method: http::Method::POST, 269 + nsid: crate::com::atproto::repo::upload_blob::NSID.into(), 270 + parameters: None, 271 + input: Some(atrium_xrpc::InputDataOrBytes::Bytes(input)), 272 + encoding, 273 + }, 274 + ) 275 + .await.map_err(Error::from)?; 276 + Ok(match response { 277 + atrium_xrpc::OutputDataOrBytes::Data(data) => Ok(data), 278 + _ => Err(GenericXrpcError::Other( 279 + "Unexpected Respose Type".to_owned(), 280 + )), 281 + }?) 282 + } 283 + 284 + pub async fn delete_record( 285 + &self, 286 + collection: Nsid, 287 + rkey: RecordKey, 288 + repo: AtIdentifier, 289 + ) -> Result<crate::com::atproto::repo::delete_record::OutputData, Error> { 290 + use crate::com::atproto::repo::delete_record::*; 291 + let result = self 292 + .api 293 + .com 294 + .atproto 295 + .repo 296 + .delete_record( 297 + InputData { 298 + collection, 299 + rkey, 300 + repo, 301 + swap_commit: None, 302 + swap_record: None, 303 + } 304 + .into(), 305 + ) 306 + .await?; 307 + Ok(result.data) 308 + } 309 + } 310 + 311 + impl<M> WeaverAgent<M> 312 + where 313 + M: CloneWithProxy + SessionManager + Send + Sync, 314 + { 315 + /// Configures the atproto-proxy header to be applied on requests. 316 + 317 + /// 318 + 319 + /// Returns a new client service with the proxy header configured. 320 + 321 + pub fn api_with_proxy(&self, did: Did, service_type: impl AsRef<str>) -> Service<Wrapper<M>> { 322 + Service::new(Arc::new( 323 + self.session_manager.clone_with_proxy(did, service_type), 324 + )) 325 + } 326 + 327 + pub fn weaver_api(&self) -> Service<Wrapper<M>> { 328 + self.api_with_proxy( 329 + Did::new("did:web:appview.weaver.sh".into()).expect("valid did"), 330 + &"atproto_weaver", 331 + ) 332 + } 333 + 334 + pub async fn get_profile_appview( 335 + &self, 336 + actor: AtIdentifier, 337 + ) -> Result<Union<ProfileDataViewInnerRefs>, Error> { 338 + use crate::sh::weaver::actor::get_profile::*; 339 + let result = self 340 + .weaver_api() 341 + .sh 342 + .weaver 343 + .actor 344 + .get_profile(ParametersData { actor }.into()) 345 + .await?; 346 + Ok(result.inner.clone()) 347 + } 348 + } 349 + 350 + impl<M> Configure for WeaverAgent<M> 351 + where 352 + M: Configure + SessionManager + Send + Sync, 353 + { 354 + fn configure_endpoint(&self, endpoint: String) { 355 + self.session_manager.configure_endpoint(endpoint); 356 + } 357 + 358 + fn configure_labelers_header(&self, labeler_dids: Option<Vec<(Did, bool)>>) { 359 + self.session_manager.configure_labelers_header(labeler_dids); 360 + } 361 + 362 + fn configure_proxy_header(&self, did: Did, service_type: impl AsRef<str>) { 363 + self.session_manager 364 + .configure_proxy_header(did, service_type); 365 + } 366 + } 367 + 368 + pub struct Wrapper<M> { 369 + inner: Arc<M>, 370 + } 371 + 372 + impl<M> Wrapper<M> 373 + where 374 + M: SessionManager + Send + Sync, 375 + { 376 + pub fn new(inner: M) -> Self { 377 + Self { 378 + inner: Arc::new(inner), 379 + } 380 + } 381 + } 382 + 383 + impl<M> HttpClient for Wrapper<M> 384 + where 385 + M: SessionManager + Send + Sync, 386 + { 387 + async fn send_http( 388 + &self, 389 + request: Request<Vec<u8>>, 390 + ) -> Result<Response<Vec<u8>>, Box<dyn std::error::Error + Send + Sync + 'static>> { 391 + self.inner.send_http(request).await 392 + } 393 + } 394 + 395 + impl<M> XrpcClient for Wrapper<M> 396 + where 397 + M: SessionManager + Send + Sync, 398 + { 399 + fn base_uri(&self) -> String { 400 + self.inner.base_uri() 401 + } 402 + async fn send_xrpc<P, I, O, E>( 403 + &self, 404 + request: &XrpcRequest<P, I>, 405 + ) -> Result<OutputDataOrBytes<O>, XrpcError<E>> 406 + where 407 + P: Serialize + Send + Sync, 408 + I: Serialize + Send + Sync, 409 + O: DeserializeOwned + Send + Sync, 410 + E: DeserializeOwned + Send + Sync + Debug, 411 + { 412 + self.inner.send_xrpc(request).await 413 + } 414 + } 415 + 416 + impl<M> SessionManager for Wrapper<M> 417 + where 418 + M: SessionManager + Send + Sync, 419 + { 420 + async fn did(&self) -> Option<Did> { 421 + self.inner.did().await 422 + } 423 + } 424 + 425 + impl<M> Configure for Wrapper<M> 426 + where 427 + M: Configure, 428 + { 429 + fn configure_endpoint(&self, endpoint: String) { 430 + self.inner.configure_endpoint(endpoint); 431 + } 432 + fn configure_labelers_header(&self, labeler_dids: Option<Vec<(Did, bool)>>) { 433 + self.inner.configure_labelers_header(labeler_dids); 434 + } 435 + fn configure_proxy_header(&self, did: Did, service_type: impl AsRef<str>) { 436 + self.inner.configure_proxy_header(did, service_type); 437 + } 438 + } 439 + 440 + impl<M> CloneWithProxy for Wrapper<M> 441 + where 442 + M: CloneWithProxy, 443 + { 444 + fn clone_with_proxy(&self, did: Did, service_type: impl AsRef<str>) -> Self { 445 + Self { 446 + inner: Arc::new(self.inner.clone_with_proxy(did, service_type)), 447 + } 448 + } 449 + } 450 + 451 + impl<M> Clone for Wrapper<M> 452 + where 453 + M: SessionManager + Send + Sync, 454 + { 455 + fn clone(&self) -> Self { 456 + Self { 457 + inner: self.inner.clone(), 458 + } 459 + } 460 + } 461 + 462 + impl<M> Deref for Wrapper<M> 463 + where 464 + M: SessionManager + Send + Sync, 465 + { 466 + type Target = M; 467 + 468 + fn deref(&self) -> &Self::Target { 469 + &self.inner 470 + } 471 + }
-67
crates/weaver-common/src/client.rs
··· 1 - use atrium_api::app::bsky::actor::defs::ProfileViewDetailedData; 2 - use atrium_api::{agent::SessionManager, types::string::AtIdentifier}; 3 - 4 - use crate::Error; 5 - 6 - pub struct WeaverHttpClient { 7 - pub client: reqwest::Client, 8 - } 9 - 10 - impl atrium_xrpc::HttpClient for WeaverHttpClient { 11 - async fn send_http( 12 - &self, 13 - request: atrium_xrpc::http::Request<Vec<u8>>, 14 - ) -> core::result::Result< 15 - atrium_xrpc::http::Response<Vec<u8>>, 16 - Box<dyn std::error::Error + Send + Sync + 'static>, 17 - > { 18 - let response = self.client.execute(request.try_into()?).await?; 19 - let mut builder = atrium_xrpc::http::Response::builder().status(response.status()); 20 - for (k, v) in response.headers() { 21 - builder = builder.header(k, v); 22 - } 23 - builder 24 - .body(response.bytes().await?.to_vec()) 25 - .map_err(Into::into) 26 - } 27 - } 28 - 29 - impl Default for WeaverHttpClient { 30 - fn default() -> Self { 31 - Self { 32 - client: reqwest::Client::new(), 33 - } 34 - } 35 - } 36 - 37 - pub struct WeaverAgent<M> 38 - where 39 - M: SessionManager + Send + Sync, 40 - { 41 - pub a: atrium_api::agent::Agent<M>, 42 - } 43 - 44 - impl<M> WeaverAgent<M> 45 - where 46 - M: SessionManager + Send + Sync, 47 - { 48 - pub fn new(session_manager: M) -> Self { 49 - Self { 50 - a: atrium_api::agent::Agent::new(session_manager), 51 - } 52 - } 53 - 54 - pub async fn get_profile(&self, actor: AtIdentifier) -> Result<ProfileViewDetailedData, Error> { 55 - use atrium_api::app::bsky::actor::get_profile::*; 56 - let resp = self 57 - .a 58 - .api 59 - .app 60 - .bsky 61 - .actor 62 - .get_profile(ParametersData { actor }.into()) 63 - .await?; 64 - 65 - Ok(resp.data) 66 - } 67 - }
-1
crates/weaver-common/src/compat.rs
··· 1 - // This module contains compatability shims for the atrium_api crate.
+35
crates/weaver-common/src/error.rs
··· 13 13 advice: Option<String>, 14 14 } 15 15 16 + impl Error { 17 + pub fn new(errors: Vec<WeaverErrorKind>) -> Self { 18 + Self { 19 + errors, 20 + advice: None, 21 + } 22 + } 23 + 24 + pub fn with_advice(mut self, advice: String) -> Self { 25 + self.advice = Some(advice); 26 + self 27 + } 28 + 29 + pub fn with_error(mut self, error: WeaverErrorKind) -> Self { 30 + self.errors.push(error); 31 + self 32 + } 33 + 34 + pub fn with_errors(mut self, errors: Error) -> Self { 35 + self.errors.extend(errors.errors); 36 + self 37 + } 38 + } 39 + 16 40 #[derive(thiserror::Error, Debug, Diagnostic)] 17 41 pub enum WeaverErrorKind { 18 42 #[error(transparent)] ··· 61 85 error: Option<String>, 62 86 }, 63 87 Other(String), 88 + } 89 + 90 + impl From<GenericXrpcError> for Error { 91 + fn from(err: GenericXrpcError) -> Self { 92 + Self { 93 + errors: vec![WeaverErrorKind::AtprotoError(AtprotoError { 94 + kind: AtprotoErrorKind::AtriumCatchall(err), 95 + })], 96 + advice: None, 97 + } 98 + } 64 99 } 65 100 66 101 impl std::fmt::Display for GenericXrpcError {
+95
crates/weaver-common/src/lexicons/client.rs
··· 24 24 pub app: app::Service<T>, 25 25 pub chat: chat::Service<T>, 26 26 pub com: com::Service<T>, 27 + pub sh: sh::Service<T>, 27 28 pub tools: tools::Service<T>, 28 29 pub(crate) _phantom: core::marker::PhantomData<T>, 29 30 } ··· 258 259 } 259 260 } 260 261 } 262 + pub mod sh { 263 + pub struct Service<T> 264 + where 265 + T: atrium_xrpc::XrpcClient + Send + Sync, 266 + { 267 + pub weaver: weaver::Service<T>, 268 + pub(crate) _phantom: core::marker::PhantomData<T>, 269 + } 270 + pub mod weaver { 271 + pub struct Service<T> 272 + where 273 + T: atrium_xrpc::XrpcClient + Send + Sync, 274 + { 275 + pub actor: actor::Service<T>, 276 + pub(crate) _phantom: core::marker::PhantomData<T>, 277 + } 278 + pub mod actor { 279 + pub struct Service<T> 280 + where 281 + T: atrium_xrpc::XrpcClient + Send + Sync, 282 + { 283 + pub(crate) xrpc: std::sync::Arc<T>, 284 + pub(crate) _phantom: core::marker::PhantomData<T>, 285 + } 286 + } 287 + } 288 + } 261 289 pub mod tools { 262 290 pub struct Service<T> 263 291 where ··· 375 403 app: app::Service::new(std::sync::Arc::clone(&xrpc)), 376 404 chat: chat::Service::new(std::sync::Arc::clone(&xrpc)), 377 405 com: com::Service::new(std::sync::Arc::clone(&xrpc)), 406 + sh: sh::Service::new(std::sync::Arc::clone(&xrpc)), 378 407 tools: tools::Service::new(std::sync::Arc::clone(&xrpc)), 379 408 _phantom: core::marker::PhantomData, 380 409 } ··· 5790 5819 .await?; 5791 5820 match response { 5792 5821 atrium_xrpc::OutputDataOrBytes::Bytes(_) => Ok(()), 5822 + _ => Err(atrium_xrpc::Error::UnexpectedResponseType), 5823 + } 5824 + } 5825 + } 5826 + impl<T> sh::Service<T> 5827 + where 5828 + T: atrium_xrpc::XrpcClient + Send + Sync, 5829 + { 5830 + #[allow(unused_variables)] 5831 + pub(crate) fn new(xrpc: std::sync::Arc<T>) -> Self { 5832 + Self { 5833 + weaver: sh::weaver::Service::new(std::sync::Arc::clone(&xrpc)), 5834 + _phantom: core::marker::PhantomData, 5835 + } 5836 + } 5837 + } 5838 + impl<T> sh::weaver::Service<T> 5839 + where 5840 + T: atrium_xrpc::XrpcClient + Send + Sync, 5841 + { 5842 + #[allow(unused_variables)] 5843 + pub(crate) fn new(xrpc: std::sync::Arc<T>) -> Self { 5844 + Self { 5845 + actor: sh::weaver::actor::Service::new(std::sync::Arc::clone(&xrpc)), 5846 + _phantom: core::marker::PhantomData, 5847 + } 5848 + } 5849 + } 5850 + impl<T> sh::weaver::actor::Service<T> 5851 + where 5852 + T: atrium_xrpc::XrpcClient + Send + Sync, 5853 + { 5854 + #[allow(unused_variables)] 5855 + pub(crate) fn new(xrpc: std::sync::Arc<T>) -> Self { 5856 + Self { 5857 + xrpc, 5858 + _phantom: core::marker::PhantomData, 5859 + } 5860 + } 5861 + ///Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth. 5862 + pub async fn get_profile( 5863 + &self, 5864 + params: crate::sh::weaver::actor::get_profile::Parameters, 5865 + ) -> atrium_xrpc::Result< 5866 + crate::sh::weaver::actor::get_profile::Output, 5867 + crate::sh::weaver::actor::get_profile::Error, 5868 + > { 5869 + let response = self 5870 + .xrpc 5871 + .send_xrpc::< 5872 + _, 5873 + (), 5874 + _, 5875 + _, 5876 + >( 5877 + &atrium_xrpc::XrpcRequest { 5878 + method: http::Method::GET, 5879 + nsid: crate::sh::weaver::actor::get_profile::NSID.into(), 5880 + parameters: Some(params), 5881 + input: None, 5882 + encoding: None, 5883 + }, 5884 + ) 5885 + .await?; 5886 + match response { 5887 + atrium_xrpc::OutputDataOrBytes::Data(data) => Ok(data), 5793 5888 _ => Err(atrium_xrpc::Error::UnexpectedResponseType), 5794 5889 } 5795 5890 }
+1
crates/weaver-common/src/lexicons/sh/weaver/actor.rs
··· 1 1 // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. 2 2 //!Definitions for the `sh.weaver.actor` namespace. 3 3 pub mod defs; 4 + pub mod get_profile; 4 5 pub mod profile; 5 6 #[derive(Debug)] 6 7 pub struct Profile;
+41
crates/weaver-common/src/lexicons/sh/weaver/actor/defs.rs
··· 2 2 //!Definitions for the `sh.weaver.actor.defs` namespace. 3 3 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 4 4 #[serde(rename_all = "camelCase")] 5 + pub struct ProfileDataViewData { 6 + pub inner: atrium_api::types::Union<ProfileDataViewInnerRefs>, 7 + } 8 + pub type ProfileDataView = atrium_api::types::Object<ProfileDataViewData>; 9 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 10 + #[serde(rename_all = "camelCase")] 5 11 pub struct ProfileViewData { 6 12 #[serde(skip_serializing_if = "core::option::Option::is_none")] 7 13 pub avatar: core::option::Option<String>, ··· 22 28 ///Free-form location text. 23 29 #[serde(skip_serializing_if = "core::option::Option::is_none")] 24 30 pub location: core::option::Option<String>, 31 + ///Pronouns to use in user-generated content. 32 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 33 + pub pronouns: core::option::Option<crate::sh::weaver::actor::profile::PronounsList>, 25 34 } 26 35 pub type ProfileView = atrium_api::types::Object<ProfileViewData>; 36 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 37 + #[serde(rename_all = "camelCase")] 38 + pub struct TangledProfileViewData { 39 + ///Include link to this account on Bluesky. 40 + pub bluesky: bool, 41 + ///Free-form profile description text. 42 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 43 + pub description: core::option::Option<String>, 44 + pub did: atrium_api::types::string::Did, 45 + pub handle: atrium_api::types::string::Handle, 46 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 47 + pub links: core::option::Option<Vec<String>>, 48 + ///Free-form location text. 49 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 50 + pub location: core::option::Option<String>, 51 + ///Any ATURI, it is up to appviews to validate these fields. 52 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 53 + pub pinned_repositories: core::option::Option<Vec<String>>, 54 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 55 + pub stats: core::option::Option<Vec<String>>, 56 + } 57 + pub type TangledProfileView = atrium_api::types::Object<TangledProfileViewData>; 58 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 59 + #[serde(tag = "$type")] 60 + pub enum ProfileDataViewInnerRefs { 61 + #[serde(rename = "sh.weaver.actor.defs#profileView")] 62 + ShWeaverActorDefsProfileView(Box<crate::sh::weaver::actor::defs::ProfileView>), 63 + #[serde(rename = "app.bsky.actor.defs#profileViewDetailed")] 64 + AppBskyActorDefsProfileViewDetailed( 65 + Box<crate::app::bsky::actor::defs::ProfileViewDetailed>, 66 + ), 67 + }
+19
crates/weaver-common/src/lexicons/sh/weaver/actor/get_profile.rs
··· 1 + // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. 2 + //!Definitions for the `sh.weaver.actor.getProfile` namespace. 3 + pub const NSID: &str = "sh.weaver.actor.getProfile"; 4 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 5 + #[serde(rename_all = "camelCase")] 6 + pub struct ParametersData { 7 + ///Handle or DID of account to fetch profile of. 8 + pub actor: atrium_api::types::string::AtIdentifier, 9 + } 10 + pub type Parameters = atrium_api::types::Object<ParametersData>; 11 + pub type Output = crate::sh::weaver::actor::defs::ProfileDataView; 12 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 13 + #[serde(tag = "error", content = "message")] 14 + pub enum Error {} 15 + impl std::fmt::Display for Error { 16 + fn fmt(&self, _f: &mut std::fmt::Formatter) -> std::fmt::Result { 17 + Ok(()) 18 + } 19 + }
+21 -2
crates/weaver-common/src/lib.rs
··· 1 - pub mod client; 2 - pub mod compat; 1 + pub mod agent; 3 2 pub mod config; 4 3 pub mod error; 5 4 pub mod lexicons; 6 5 pub mod oauth; 7 6 pub mod resolver; 7 + pub mod xrpc_server; 8 + use atrium_api::types::{BlobRef, TypedBlobRef, string::Did}; 8 9 pub use lexicons::*; 9 10 10 11 pub use crate::error::{Error, IoError, ParseError, SerDeError}; ··· 34 35 markdown_weaver::CowStr::Inlined(s) => std::borrow::Cow::Owned(s.as_ref().to_owned()), 35 36 } 36 37 } 38 + 39 + pub fn avatar_cdn_url(did: &Did, blob_ref: &BlobRef) -> String { 40 + let (cid, mime_type) = match blob_ref { 41 + BlobRef::Typed(TypedBlobRef::Blob(b)) => ( 42 + atrium_api::types::string::Cid::new(b.r#ref.0) 43 + .as_ref() 44 + .to_string(), 45 + &b.mime_type, 46 + ), 47 + BlobRef::Untyped(r) => (r.cid.clone(), &r.mime_type), 48 + }; 49 + format!( 50 + "https://cdn.bsky.app/img/avatar/plain/{}/{}@{}", 51 + did.as_str(), 52 + cid, 53 + mime_type.strip_prefix("image/").unwrap_or(mime_type) 54 + ) 55 + }
+1 -1
crates/weaver-common/src/oauth.rs
··· 21 21 22 22 use std::sync::Arc; 23 23 24 - use crate::{client::WeaverHttpClient, resolver::HickoryDnsTxtResolver}; 24 + use crate::{agent::WeaverHttpClient, resolver::HickoryDnsTxtResolver}; 25 25 26 26 pub struct NativeOAuthClient { 27 27 oauth: NativeBasicOAuthClient,
+9
crates/weaver-common/src/xrpc_server.rs
··· 1 + pub async fn serve<T, E>( 2 + namespace: String, 3 + ) -> Result<atrium_xrpc::OutputDataOrBytes<T>, atrium_xrpc::Error<E>> 4 + where 5 + T: serde::de::DeserializeOwned, 6 + E: serde::de::DeserializeOwned + std::fmt::Debug, 7 + { 8 + todo!() 9 + }
+3
crates/weaver-renderer/Cargo.toml
··· 7 7 8 8 [dependencies] 9 9 n0-future = { workspace = true } 10 + atrium-api = { version = "0.25.3", default-features = false } 10 11 11 12 weaver-common = { path = "../weaver-common" } 13 + 12 14 weaver-workspace-hack = { version = "0.1", path = "../weaver-workspace-hack" } 15 + compact_string = "0.1.0"
+5
crates/weaver-renderer/src/lib.rs
··· 1 + //! Weaver renderer 2 + //! 3 + //! This crate works with the weaver-markdown crate to render and optionally upload markdown notebooks to your ATProto PDS. 4 + //! 1 5 6 + pub mod types;
+112
crates/weaver-renderer/src/types.rs
··· 1 + use atrium_api::types::string::{Cid, Did}; 2 + use compact_string::CompactString; 3 + 4 + pub type MimeType = CompactString; 5 + 6 + pub enum BlobLink { 7 + PDS { 8 + pds_host: String, 9 + did: Did, 10 + cid: Cid, 11 + mime_type: MimeType, 12 + }, 13 + BskyCdn(CdnLink), 14 + WeaverCdn(CdnLink), 15 + } 16 + 17 + pub enum CdnLink { 18 + Avatar(Did, Cid, MimeType), 19 + Banner(Did, Cid, MimeType), 20 + Thumbnail(Did, Cid, MimeType), 21 + PostImage(Did, Cid, MimeType), 22 + EmbedImage(Did, Cid, MimeType), 23 + } 24 + 25 + impl std::fmt::Display for BlobLink { 26 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 27 + write!( 28 + f, 29 + "{}{}/{}@{}", 30 + self.url_prefix(), 31 + self.did().as_str(), 32 + self.cid().as_ref(), 33 + self.mime_type().rsplit('/').next().unwrap() 34 + ) 35 + } 36 + } 37 + 38 + impl BlobLink { 39 + #[inline] 40 + pub fn url_prefix(&self) -> &str { 41 + match self { 42 + BlobLink::PDS { pds_host, .. } => pds_host, 43 + BlobLink::BskyCdn(CdnLink::Avatar(..)) => "https://cdn.bsky.app/img/avatar/plain/", 44 + BlobLink::BskyCdn(CdnLink::Banner(..)) => "https://cdn.bsky.app/img/banner/plain/", 45 + BlobLink::BskyCdn(CdnLink::Thumbnail(..)) => { 46 + "https://cdn.bsky.app/img/feed_thumbnail/plain/" 47 + } 48 + BlobLink::BskyCdn(CdnLink::PostImage(..)) => { 49 + "https://cdn.bsky.app/img/feed_fullsize/plain/" 50 + } 51 + BlobLink::BskyCdn(CdnLink::EmbedImage(..)) => { 52 + "https://cdn.bsky.app/img/feed_fullsize/plain/" 53 + } 54 + BlobLink::WeaverCdn(CdnLink::Avatar(..)) => "https://cdn.weaver.sh/img/avatar/", 55 + BlobLink::WeaverCdn(CdnLink::Banner(..)) => "https://cdn.weaver.sh/img/full/", 56 + BlobLink::WeaverCdn(CdnLink::Thumbnail(..)) => "https://cdn.weaver.sh/img/thumbnail/", 57 + BlobLink::WeaverCdn(CdnLink::PostImage(..)) => "https://cdn.weaver.sh/img/full/", 58 + BlobLink::WeaverCdn(CdnLink::EmbedImage(..)) => "https://cdn.weaver.sh/img/embed/", 59 + } 60 + } 61 + 62 + #[inline] 63 + pub const fn did(&self) -> &Did { 64 + match self { 65 + BlobLink::PDS { did, .. } => did, 66 + BlobLink::BskyCdn(CdnLink::Avatar(did, ..)) 67 + | BlobLink::BskyCdn(CdnLink::Banner(did, ..)) 68 + | BlobLink::BskyCdn(CdnLink::Thumbnail(did, ..)) 69 + | BlobLink::BskyCdn(CdnLink::PostImage(did, ..)) 70 + | BlobLink::BskyCdn(CdnLink::EmbedImage(did, ..)) => did, 71 + BlobLink::WeaverCdn(CdnLink::Avatar(did, ..)) 72 + | BlobLink::WeaverCdn(CdnLink::Banner(did, ..)) 73 + | BlobLink::WeaverCdn(CdnLink::Thumbnail(did, ..)) 74 + | BlobLink::WeaverCdn(CdnLink::PostImage(did, ..)) 75 + | BlobLink::WeaverCdn(CdnLink::EmbedImage(did, ..)) => did, 76 + } 77 + } 78 + 79 + #[inline] 80 + pub const fn cid(&self) -> &Cid { 81 + match self { 82 + BlobLink::PDS { cid, .. } => cid, 83 + BlobLink::BskyCdn(CdnLink::Avatar(_, cid, ..)) 84 + | BlobLink::BskyCdn(CdnLink::Banner(_, cid, ..)) 85 + | BlobLink::BskyCdn(CdnLink::Thumbnail(_, cid, ..)) 86 + | BlobLink::BskyCdn(CdnLink::PostImage(_, cid, ..)) 87 + | BlobLink::BskyCdn(CdnLink::EmbedImage(_, cid, ..)) => cid, 88 + BlobLink::WeaverCdn(CdnLink::Avatar(_, cid, ..)) 89 + | BlobLink::WeaverCdn(CdnLink::Banner(_, cid, ..)) 90 + | BlobLink::WeaverCdn(CdnLink::Thumbnail(_, cid, ..)) 91 + | BlobLink::WeaverCdn(CdnLink::PostImage(_, cid, ..)) 92 + | BlobLink::WeaverCdn(CdnLink::EmbedImage(_, cid, ..)) => cid, 93 + } 94 + } 95 + 96 + #[inline] 97 + pub const fn mime_type(&self) -> &MimeType { 98 + match self { 99 + BlobLink::PDS { mime_type, .. } => mime_type, 100 + BlobLink::BskyCdn(CdnLink::Avatar(_, _, mime_type)) 101 + | BlobLink::BskyCdn(CdnLink::Banner(_, _, mime_type)) 102 + | BlobLink::BskyCdn(CdnLink::Thumbnail(_, _, mime_type)) 103 + | BlobLink::BskyCdn(CdnLink::PostImage(_, _, mime_type)) 104 + | BlobLink::BskyCdn(CdnLink::EmbedImage(_, _, mime_type)) => mime_type, 105 + BlobLink::WeaverCdn(CdnLink::Avatar(_, _, mime_type)) 106 + | BlobLink::WeaverCdn(CdnLink::Banner(_, _, mime_type)) 107 + | BlobLink::WeaverCdn(CdnLink::Thumbnail(_, _, mime_type)) 108 + | BlobLink::WeaverCdn(CdnLink::PostImage(_, _, mime_type)) 109 + | BlobLink::WeaverCdn(CdnLink::EmbedImage(_, _, mime_type)) => mime_type, 110 + } 111 + } 112 + }
+2 -2
crates/weaver-server/Cargo.toml
··· 9 9 weaver-common = { path = "../weaver-common" } 10 10 weaver-workspace-hack = { version = "0.1", path = "../weaver-workspace-hack" } 11 11 12 - axum = "0.7.5" 12 + axum = "0.8" 13 13 tokio = { version = "1.44", features = ["full"] } 14 - 14 + notify = "8.0" 15 15 16 16 clap = { version = "4.5", features = ["derive", "env", "cargo", "unicode"] }
+13
crates/weaver-server/src/main.rs
··· 1 + //! Weaver server 2 + //! 3 + //! This crate is a lightweight HTTP server which can serve a notebook. 4 + //! It will auto-reload 5 + 6 + use axum::{Router, response::Html, routing::get}; 7 + 8 + use tokio::net::TcpListener; 9 + 1 10 #[tokio::main] 2 11 async fn main() {} 12 + 13 + async fn handler() -> Html<&'static str> { 14 + Html("<h1>Hello, World!</h1>") 15 + }
+44 -5
crates/weaver-workspace-hack/Cargo.toml
··· 16 16 17 17 ### BEGIN HAKARI SECTION 18 18 [dependencies] 19 + base16ct = { version = "0.2", default-features = false, features = ["alloc"] } 20 + byteorder = { version = "1" } 21 + chrono = { version = "0.4", features = ["serde"] } 19 22 data-encoding = { version = "2" } 20 - futures-channel = { version = "0.3" } 21 - futures-core = { version = "0.3" } 23 + der = { version = "0.7", default-features = false, features = ["oid", "pem"] } 24 + diesel = { version = "2", features = ["chrono", "i-implement-a-third-party-backend-and-opt-into-breaking-changes", "postgres", "serde_json", "sqlite"] } 25 + diesel-async = { version = "0.5", features = ["deadpool", "postgres", "sync-connection-wrapper"] } 26 + ecdsa = { version = "0.16", features = ["alloc", "der", "signing", "verifying"] } 27 + either = { version = "1", features = ["use_std"] } 28 + elliptic-curve = { version = "0.13", features = ["digest", "hazmat", "jwk", "pem"] } 29 + fastrand = { version = "2" } 30 + ff = { version = "0.13", default-features = false, features = ["alloc"] } 31 + futures-channel = { version = "0.3", features = ["sink"] } 32 + generic-array = { version = "0.14", default-features = false, features = ["more_lengths", "serde", "zeroize"] } 33 + group = { version = "0.13", default-features = false, features = ["alloc"] } 34 + hashbrown = { version = "0.15", features = ["serde"] } 22 35 idna = { version = "1" } 23 - memchr = { version = "2" } 36 + libsqlite3-sys = { version = "0.30", features = ["bundled"] } 37 + log = { version = "0.4", default-features = false, features = ["std"] } 38 + memchr = { version = "2", features = ["use_std"] } 39 + miette = { version = "7", features = ["fancy", "syntect-highlighter"] } 40 + mime_guess = { version = "2" } 41 + minijinja = { version = "2" } 42 + num-traits = { version = "0.2", default-features = false, features = ["i128", "libm", "std"] } 43 + p384 = { version = "0.13", default-features = false, features = ["ecdsa"] } 24 44 percent-encoding = { version = "2" } 25 - proc-macro2 = { version = "1" } 45 + pkcs8 = { version = "0.10", default-features = false, features = ["pem"] } 46 + proc-macro2 = { version = "1", features = ["span-locations"] } 26 47 quote = { version = "1" } 48 + rand_core = { version = "0.6", default-features = false, features = ["std"] } 49 + reqwest = { version = "0.12", features = ["gzip", "json", "rustls-tls"] } 27 50 scopeguard = { version = "1" } 51 + sec1 = { version = "0.7", features = ["pem", "serde", "subtle"] } 52 + serde = { version = "1", features = ["alloc", "derive"] } 53 + serde_json = { version = "1", features = ["alloc", "preserve_order", "raw_value"] } 54 + sha2 = { version = "0.10", features = ["oid"] } 28 55 smallvec = { version = "1", default-features = false, features = ["const_new"] } 56 + spki = { version = "0.7", default-features = false, features = ["pem"] } 57 + subtle = { version = "2" } 29 58 syn = { version = "2", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } 59 + time = { version = "0.3", features = ["formatting", "local-offset", "macros", "parsing"] } 30 60 tokio = { version = "1", features = ["full"] } 61 + tokio-util = { version = "0.7", features = ["codec", "io"] } 62 + toml = { version = "0.8", features = ["preserve_order"] } 31 63 tracing = { version = "0.1", features = ["log"] } 64 + tracing-core = { version = "0.1" } 65 + zeroize = { version = "1", features = ["derive", "serde"] } 32 66 33 67 [build-dependencies] 34 68 data-encoding = { version = "2" } 35 - proc-macro2 = { version = "1" } 69 + diesel_derives = { version = "2", features = ["32-column-tables", "chrono", "postgres", "sqlite", "with-deprecated"] } 70 + either = { version = "1", features = ["use_std"] } 71 + hashbrown = { version = "0.15", features = ["serde"] } 72 + proc-macro2 = { version = "1", features = ["span-locations"] } 36 73 quote = { version = "1" } 74 + serde = { version = "1", features = ["alloc", "derive"] } 37 75 syn = { version = "2", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } 76 + toml = { version = "0.8", features = ["preserve_order"] } 38 77 39 78 ### END HAKARI SECTION
+29 -15
flake.lock
··· 3 3 "advisory-db": { 4 4 "flake": false, 5 5 "locked": { 6 - "lastModified": 1744288177, 7 - "narHash": "sha256-HxYhdWc6YJ8Q8gOZtv7+teCucmDf6+Ntickvb62Au58=", 6 + "lastModified": 1747489415, 7 + "narHash": "sha256-CLQVUrdiRhJx0JppNZxx8FhgUbcliQUIYVI9Aclk39g=", 8 8 "owner": "rustsec", 9 9 "repo": "advisory-db", 10 - "rev": "1273f0099ce6882659ff64b852c8fdb5f8cdd5b9", 10 + "rev": "f16bc6329058299fb558e8f78eb3b065a4d14ea5", 11 11 "type": "github" 12 12 }, 13 13 "original": { ··· 18 18 }, 19 19 "crane": { 20 20 "locked": { 21 - "lastModified": 1745022865, 22 - "narHash": "sha256-tXL4qUlyYZEGOHUKUWjmmcvJjjLQ+4U38lPWSc8Cgdo=", 21 + "lastModified": 1747587869, 22 + "narHash": "sha256-Zay3WJdSvC2VQmNqWSVLBOg/1iS/0/Q0c9JOBsB+3qw=", 23 23 "owner": "ipetkov", 24 24 "repo": "crane", 25 - "rev": "25ca4c50039d91ad88cc0b8feacb9ad7f748dedf", 25 + "rev": "76603d32f18e0e378d9f6335c8fc286413493655", 26 26 "type": "github" 27 27 }, 28 28 "original": { ··· 51 51 }, 52 52 "nixpkgs": { 53 53 "locked": { 54 - "lastModified": 1744868846, 55 - "narHash": "sha256-5RJTdUHDmj12Qsv7XOhuospjAjATNiTMElplWnJE9Hs=", 54 + "lastModified": 1747467164, 55 + "narHash": "sha256-JBXbjJ0t6T6BbVc9iPVquQI9XSXCGQJD8c8SgnUquus=", 56 56 "owner": "NixOS", 57 57 "repo": "nixpkgs", 58 - "rev": "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c", 58 + "rev": "3fcbdcfc707e0aa42c541b7743e05820472bdaec", 59 + "type": "github" 60 + }, 61 + "original": { 62 + "owner": "NixOS", 63 + "ref": "nixpkgs-unstable", 64 + "repo": "nixpkgs", 65 + "type": "github" 66 + } 67 + }, 68 + "nixpkgs_2": { 69 + "locked": { 70 + "lastModified": 1744536153, 71 + "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", 72 + "owner": "NixOS", 73 + "repo": "nixpkgs", 74 + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", 59 75 "type": "github" 60 76 }, 61 77 "original": { ··· 76 92 }, 77 93 "rust-overlay": { 78 94 "inputs": { 79 - "nixpkgs": [ 80 - "nixpkgs" 81 - ] 95 + "nixpkgs": "nixpkgs_2" 82 96 }, 83 97 "locked": { 84 - "lastModified": 1745375696, 85 - "narHash": "sha256-zFRCHNRJ1Ei8GD3iP+wcedkJzLo84OO7JEu/gmhSllU=", 98 + "lastModified": 1747535902, 99 + "narHash": "sha256-rKWBtLcqQeu8PpfKIBn1ORXS2udSH/MwnQFwfSpFOLg=", 86 100 "owner": "oxalica", 87 101 "repo": "rust-overlay", 88 - "rev": "19d1b5002d43c019880dd5cc0f8420efd20d5faf", 102 + "rev": "b7a99615d26b82c39b73ccc9026545c3f3403b71", 89 103 "type": "github" 90 104 }, 91 105 "original": {
+14 -55
flake.nix
··· 3 3 4 4 inputs = { 5 5 nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 6 - 7 - crane.url = "github:ipetkov/crane"; 8 - 9 6 flake-utils.url = "github:numtide/flake-utils"; 10 7 11 8 advisory-db = { 12 9 url = "github:rustsec/advisory-db"; 13 10 flake = false; 14 11 }; 15 - rust-overlay = { 16 - url = "github:oxalica/rust-overlay"; 17 - inputs.nixpkgs.follows = "nixpkgs"; 18 - }; 12 + 13 + rust-overlay.url = "github:oxalica/rust-overlay"; 14 + crane.url = "github:ipetkov/crane"; 19 15 }; 16 + 20 17 21 18 outputs = { 22 19 self, ··· 37 34 inherit (pkgs) lib; 38 35 39 36 rustToolchainFor = p: 40 - p.rust-bin.stable.latest.default.override { 37 + p.rust-bin.selectLatestNightlyWith(toolchain: toolchain.default.override { 41 38 # Set the build targets supported by the toolchain, 42 39 # wasm32-unknown-unknown is required for trunk. 43 - targets = ["wasm32-unknown-unknown"]; 44 - extensions = ["llvm-tools"]; 45 - }; 40 + #targets = ["wasm32-unknown-unknown"]; 41 + extensions = [ 42 + "llvm-tools" 43 + "rust-src" 44 + "rust-analyzer" 45 + "clippy" 46 + ]; 47 + }); 46 48 craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchainFor; 47 - # When filtering sources, we want to allow assets other than .rs files 48 - unfilteredRoot = ./.; # The original, unfiltered source 49 - src = lib.fileset.toSource { 50 - root = unfilteredRoot; 51 - fileset = lib.fileset.unions [ 52 - # Default files from crane (Rust and cargo files) 53 - (craneLib.fileset.commonCargoSources unfilteredRoot) 54 - ( 55 - lib.fileset.fileFilter 56 - (file: lib.any file.hasExt ["html" "scss"]) 57 - unfilteredRoot 58 - ) 59 - # Example of a folder for images, icons, etc 60 - (lib.fileset.maybeMissing ./assets) 61 - ]; 62 - }; 49 + src = craneLib.cleanCargoSource ./.; 63 50 64 51 # Common arguments can be set here to avoid repeating them later 65 52 commonArgs = { ··· 85 72 # Additional environment variables can be set directly 86 73 # MY_CUSTOM_VAR = "some value"; 87 74 }; 88 - 89 - # Wasm packages 90 - 91 - # it's not possible to build the server on the 92 - # wasm32 target, so we only build the client. 93 - wasmArgs = 94 - commonArgs 95 - // { 96 - pname = "trunk-workspace-wasm"; 97 - cargoExtraArgs = "--package=client"; 98 - CARGO_BUILD_TARGET = "wasm32-unknown-unknown"; 99 - }; 100 - 101 - cargoArtifactsWasm = craneLib.buildDepsOnly (wasmArgs 102 - // { 103 - doCheck = false; 104 - }); 105 - 106 - # craneLibLLvmTools = craneLib.overrideToolchain 107 - # (fenix.packages.${system}.complete.withComponents [ 108 - # "cargo" 109 - # "llvm-tools" 110 - # "rustc" 111 - # ]); 112 75 113 76 # Build *just* the cargo dependencies (of the entire workspace), 114 77 # so we can reuse all of that work (e.g. via cachix) when running in CI ··· 232 195 233 196 nativeBuildInputs = with pkgs; [ 234 197 cargo-hakari 235 - sqlite 236 - pkg-config 237 - openssl 238 198 ]; 239 199 }; 240 200 }; ··· 284 244 alejandra 285 245 diesel-cli 286 246 postgresql 287 - cargo-watch 288 247 jq 289 248 ]; 290 249 };
+76
lexicons/sh/weaver/actor/defs.json
··· 41 41 "labels": { 42 42 "type": "array", 43 43 "items": { "type": "ref", "ref": "com.atproto.label.defs#label" } 44 + }, 45 + "pronouns": { 46 + "type": "ref", 47 + "description": "Pronouns to use in user-generated content.", 48 + "ref": "sh.weaver.actor.profile#pronounsList" 49 + } 50 + } 51 + }, 52 + "profileDataView": { 53 + "type": "object", 54 + "required": ["inner"], 55 + "properties": { 56 + "inner": { 57 + "type": "union", 58 + "refs": ["sh.weaver.actor.defs#profileView", "app.bsky.actor.defs#profileViewDetailed"] 59 + } 60 + } 61 + }, 62 + "tangledProfileView": { 63 + "type": "object", 64 + "required": ["bluesky", "did", "handle"], 65 + "properties": { 66 + "did": { "type": "string", "format": "did" }, 67 + "handle": { "type": "string", "format": "handle" }, 68 + "description": { 69 + "type": "string", 70 + "description": "Free-form profile description text.", 71 + "maxGraphemes": 256, 72 + "maxLength": 2560 73 + }, 74 + "links": { 75 + "type": "array", 76 + "minLength": 0, 77 + "maxLength": 5, 78 + "items": { 79 + "type": "string", 80 + "description": "Any URI, intended for social profiles or websites, can be used to link DIDs/AT-URIs too.", 81 + "format": "uri" 82 + } 83 + }, 84 + "stats": { 85 + "type": "array", 86 + "minLength": 0, 87 + "maxLength": 2, 88 + "items": { 89 + "type": "string", 90 + "description": "Vanity stats.", 91 + "enum": [ 92 + "merged-pull-request-count", 93 + "closed-pull-request-count", 94 + "open-pull-request-count", 95 + "open-issue-count", 96 + "closed-issue-count", 97 + "repository-count" 98 + ] 99 + } 100 + }, 101 + "bluesky": { 102 + "type": "boolean", 103 + "description": "Include link to this account on Bluesky." 104 + }, 105 + "location": { 106 + "type": "string", 107 + "description": "Free-form location text.", 108 + "maxGraphemes": 40, 109 + "maxLength": 400 110 + }, 111 + "pinnedRepositories": { 112 + "type": "array", 113 + "description": "Any ATURI, it is up to appviews to validate these fields.", 114 + "minLength": 0, 115 + "maxLength": 6, 116 + "items": { 117 + "type": "string", 118 + "format": "at-uri" 119 + } 44 120 } 45 121 } 46 122 }
+28
lexicons/sh/weaver/actor/getProfile.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.weaver.actor.getProfile", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth.", 8 + "parameters": { 9 + "type": "params", 10 + "required": ["actor"], 11 + "properties": { 12 + "actor": { 13 + "type": "string", 14 + "format": "at-identifier", 15 + "description": "Handle or DID of account to fetch profile of." 16 + } 17 + } 18 + }, 19 + "output": { 20 + "encoding": "application/json", 21 + "schema": { 22 + "type": "ref", 23 + "ref": "sh.weaver.actor.defs#profileDataView" 24 + } 25 + } 26 + } 27 + } 28 + }