wip: Custom mirroring tangled knot written in Rust
rust tangled mirror knot
1

Configure Feed

Select the types of activity you want to include in your feed.

xrpc: generate typesafe lexicons

Clément (Jun 9, 2026, 8:30 PM +0200) b8b47857 1567febc

+540 -5
+21
Cargo.lock
··· 2120 2120 "jacquard", 2121 2121 "jacquard-axum", 2122 2122 "k256", 2123 + "knotty-lexicons", 2123 2124 "knotty-web", 2124 2125 "miette", 2125 2126 "multibase", ··· 2130 2131 "tower-http", 2131 2132 "tracing", 2132 2133 "tracing-subscriber", 2134 + ] 2135 + 2136 + [[package]] 2137 + name = "knotty-lexgen" 2138 + version = "0.0.0" 2139 + dependencies = [ 2140 + "jacquard-lexicon", 2141 + "miette", 2142 + ] 2143 + 2144 + [[package]] 2145 + name = "knotty-lexicons" 2146 + version = "0.0.0" 2147 + dependencies = [ 2148 + "jacquard-common", 2149 + "jacquard-derive", 2150 + "jacquard-lexicon", 2151 + "miette", 2152 + "serde", 2153 + "thiserror 2.0.18", 2133 2154 ] 2134 2155 2135 2156 [[package]]
+11
justfile
··· 1 + # Regenerate the lexicon bindings for both ends from the shared `lexicons/` 2 + # corpus. Run after editing anything under `lexicons/`. 3 + codegen: codegen-rust codegen-ts 4 + 5 + # Rust backend bindings -> crates/knotty-lexicons/src 6 + codegen-rust: 7 + cargo run -p knotty-lexgen 8 + 9 + # SPA client bindings -> app/src/generated/lexicons 10 + codegen-ts: 11 + pnpm --filter app run lexicon:generate
+166
pnpm-lock.yaml
··· 228 228 '@atcute/identity-resolver': 229 229 specifier: ^2.0.0 230 230 version: 2.0.0(@atcute/identity@2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/lexicons@2.0.0)(typescript@5.9.3) 231 + '@atcute/lex-cli': 232 + specifier: ^3.0.1 233 + version: 3.1.0(@atcute/cbor@2.3.3(@atcute/cid@2.4.1))(@atcute/cid@2.4.1)(typescript@5.9.3) 234 + '@atcute/lexicon-doc': 235 + specifier: ^3.0.0 236 + version: 3.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3) 231 237 '@atcute/lexicons': 232 238 specifier: ^2.0.0 233 239 version: 2.0.0 ··· 288 294 peerDependencies: 289 295 '@atcute/lexicons': ^2.0.0 290 296 297 + '@atcute/car@6.0.0': 298 + resolution: {integrity: sha512-v3lhHfqxBi/wxq3q4N9f3/9ed44xqwKI0VAlgPge9NKjJ2xuJcEvj12v4m+P2FB758cioCsghKy58tz2s20eAA==} 299 + peerDependencies: 300 + '@atcute/cbor': ^2.0.0 301 + '@atcute/cid': ^2.0.0 302 + 303 + '@atcute/cbor@2.3.3': 304 + resolution: {integrity: sha512-zZ4nHOK837zTMWJtta35YD7pcukrTzDc8jkpIGlSgoDYzu3l4BX3WVgpPJtRn3K6h2v97uyiWfiVjSpM7JSFzQ==} 305 + peerDependencies: 306 + '@atcute/cid': ^2.0.0 307 + 308 + '@atcute/cid@2.4.1': 309 + resolution: {integrity: sha512-bwhna69RCv7yetXudtj+2qrMPYvhhIQqvJz6YUpUS98v7OdF3X2dnye9Nig2NDrklZcuyOsu7sQo7GOykJXRLQ==} 310 + 291 311 '@atcute/client@5.0.0': 292 312 resolution: {integrity: sha512-Dtrc1no1oAtpUTmkBxH0xKSgy8qcnk8orPf/PkEATW+MB3k8FPHtPH2fshiKd55rioZkb+xaN+7A29WtqPQHRA==} 293 313 peerDependencies: 294 314 '@atcute/lexicons': ^2.0.0 315 + 316 + '@atcute/crypto@2.4.1': 317 + resolution: {integrity: sha512-tJ3Pi/XYcAsABKtqSlSOTKfO5YiQ4XdqlTuPS8HiRZSezOPcXBFFzAFWpSIJPURbVPFQL3LLrrK0Ea24wl5qeQ==} 295 318 296 319 '@atcute/identity-resolver@2.0.0': 297 320 resolution: {integrity: sha512-IKg1BDQAF2bIdN10DL6KAXmTjK+3enTU2IRbuani9TsFahBwGZ7O5FiVmTiL6QlGfauGNW5S0xNCOxWXWMoR2Q==} ··· 304 327 peerDependencies: 305 328 '@atcute/lexicons': ^2.0.0 306 329 330 + '@atcute/lex-cli@3.1.0': 331 + resolution: {integrity: sha512-Mba7y12ENIbdj+cr+gt5mJTalpuz/UbS4ykm/wT7t9ipoJtidXMSeuQFfeoyBvvidW77Vg4G6zxZkoDxtlr6Mg==} 332 + hasBin: true 333 + 334 + '@atcute/lexicon-doc@3.0.0': 335 + resolution: {integrity: sha512-hxBTEvO78X4j/HsGgcH8GKgMA9hP9FM6jtwQL8Yd6MIHtGXdB8QygvBmOAJEqm1oE9ckU1SSU3cwa2t4tbZZNw==} 336 + peerDependencies: 337 + '@atcute/lexicons': ^2.0.0 338 + 339 + '@atcute/lexicon-resolver@1.0.0': 340 + resolution: {integrity: sha512-4v3oSijgd3w9JS2BMsV1dfD+GD1ClQeuEJatM9b7rNffi1kMTSn/3nOQbJ1aNeXfqZbCkn3ATDRdvgki1xIAZw==} 341 + peerDependencies: 342 + '@atcute/identity': ^2.0.0 343 + '@atcute/identity-resolver': ^2.0.0 344 + '@atcute/lexicon-doc': ^3.0.0 345 + '@atcute/lexicons': ^2.0.0 346 + 307 347 '@atcute/lexicons@2.0.0': 308 348 resolution: {integrity: sha512-fIlwP+TPEAGoF5aU5s+f8N5sOjOu8Mww/sQL1B57Dp2hj3G/EWG9XwOHPokzycBCgXx+UxIIrzZCGy8whsVDZw==} 349 + 350 + '@atcute/mst@1.0.1': 351 + resolution: {integrity: sha512-F3PfHt/6RedpSCwPFVOK8YQ+9lohAqeqUfqWrXLEZUmSVjXPlhSY6re+fmAnNuYX1g124QprIea9G2K3IfymyQ==} 352 + peerDependencies: 353 + '@atcute/cbor': ^2.0.0 354 + '@atcute/cid': ^2.0.0 309 355 310 356 '@atcute/multibase@1.2.0': 311 357 resolution: {integrity: sha512-ZK2GRra+qIYq9nNuQB52m2ul0hOmCQEtPobGfTSUxm7pF0OGEkWGkWHugFhNEDVzHzTwPxHp6VGotdZFue4lYQ==} ··· 325 371 '@atcute/oauth-types@1.0.0': 326 372 resolution: {integrity: sha512-YOpjLU8H5PG6oKfgau+dx7rSmGsLxIA36MeGL7BDeopcyq80RqPSBAzOasEEsmbMRJ/nTsMRJhnmGkp3RCa/Zw==} 327 373 374 + '@atcute/repo@1.0.0': 375 + resolution: {integrity: sha512-3s6VDKMimmYxVXn9OTlYQ7bJGPRcZRyFZ+oF/IFdfm3PsEUxwwSXajw1bG0HVXns1rNgNnDUT0PqMlUd2GnjqA==} 376 + peerDependencies: 377 + '@atcute/cbor': ^2.0.0 378 + '@atcute/cid': ^2.0.0 379 + '@atcute/lexicons': ^2.0.0 380 + 328 381 '@atcute/tangled@2.0.5': 329 382 resolution: {integrity: sha512-HMWpFd4ePUaV4t7e8Mn/Gq4T3Qw9Xki4SGHhS1RoXQer7mIbdhwJzXpbJGTL/Cg8ipTPpVINIDtq3sWyetCjHA==} 330 383 peerDependencies: ··· 338 391 339 392 '@atcute/util-text@1.3.1': 340 393 resolution: {integrity: sha512-MRgJXkx67znuBXuoAYCJkBZyd3OApL7zZlNf5kXhuoCXcdiu1nblRDycYTADSkym4epBSQWxh26kmI9sewaq6A==} 394 + 395 + '@atcute/varint@2.0.0': 396 + resolution: {integrity: sha512-CEY/oVK/nVpL4e5y3sdenLETDL6/Xu5xsE/0TupK+f0Yv8jcD60t2gD8SHROWSvUwYLdkjczLCSA7YrtnjCzWw==} 341 397 342 398 '@babel/code-frame@7.29.7': 343 399 resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} ··· 613 669 '@jridgewell/trace-mapping@0.3.31': 614 670 resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} 615 671 672 + '@noble/secp256k1@3.1.0': 673 + resolution: {integrity: sha512-+F7iS7tUMaNGXcc9X3PjmjvuQnXEuSjCRNzVVA2xAcKXgCaP0dHYz4SFyt4FKNHef7sOP//xihowcySSS7PK9g==} 674 + 616 675 '@nothing-but/utils@0.17.0': 617 676 resolution: {integrity: sha512-TuCHcHLOqDL0SnaAxACfuRHBNRgNJcNn9X0GiH5H3YSDBVquCr3qEIG3FOQAuMyZCbu9w8nk2CHhOsn7IvhIwQ==} 677 + 678 + '@optique/core@1.0.2': 679 + resolution: {integrity: sha512-znsqMmjAdeOgSJzdJlpZpgAscojwQmeQYXzYnuEKllz5VCj6WyEkdzU4QuvJQtWQY3ve2taXwudEBRur0VHBOQ==} 680 + engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} 681 + 682 + '@optique/run@1.0.2': 683 + resolution: {integrity: sha512-0Wc+zC8SLGV8zXQX+pk+o0c6wE/ddx/36CHZ0toTh5lApsjruUuGhqbxvljerAAG5un1xQbOLxzksBVC6UPgSg==} 684 + engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} 618 685 619 686 '@rollup/rollup-android-arm-eabi@4.61.1': 620 687 resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==} ··· 1566 1633 resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} 1567 1634 engines: {node: ^10 || ^12 || >=14} 1568 1635 1636 + prettier@3.8.3: 1637 + resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} 1638 + engines: {node: '>=14'} 1639 + hasBin: true 1640 + 1569 1641 proxy-compare@3.0.1: 1570 1642 resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} 1571 1643 ··· 1797 1869 '@atcute/atproto': 4.0.2(@atcute/lexicons@2.0.0) 1798 1870 '@atcute/lexicons': 2.0.0 1799 1871 1872 + '@atcute/car@6.0.0(@atcute/cbor@2.3.3(@atcute/cid@2.4.1))(@atcute/cid@2.4.1)': 1873 + dependencies: 1874 + '@atcute/cbor': 2.3.3(@atcute/cid@2.4.1) 1875 + '@atcute/cid': 2.4.1 1876 + '@atcute/uint8array': 1.1.2 1877 + '@atcute/varint': 2.0.0 1878 + 1879 + '@atcute/cbor@2.3.3(@atcute/cid@2.4.1)': 1880 + dependencies: 1881 + '@atcute/cid': 2.4.1 1882 + '@atcute/multibase': 1.2.0 1883 + '@atcute/uint8array': 1.1.2 1884 + 1885 + '@atcute/cid@2.4.1': 1886 + dependencies: 1887 + '@atcute/multibase': 1.2.0 1888 + '@atcute/uint8array': 1.1.2 1889 + 1800 1890 '@atcute/client@5.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3)': 1801 1891 dependencies: 1802 1892 '@atcute/identity': 2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3) 1803 1893 '@atcute/lexicons': 2.0.0 1804 1894 transitivePeerDependencies: 1805 1895 - typescript 1896 + 1897 + '@atcute/crypto@2.4.1': 1898 + dependencies: 1899 + '@atcute/multibase': 1.2.0 1900 + '@atcute/uint8array': 1.1.2 1901 + '@noble/secp256k1': 3.1.0 1806 1902 1807 1903 '@atcute/identity-resolver@2.0.0(@atcute/identity@2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/lexicons@2.0.0)(typescript@5.9.3)': 1808 1904 dependencies: ··· 1820 1916 transitivePeerDependencies: 1821 1917 - typescript 1822 1918 1919 + '@atcute/lex-cli@3.1.0(@atcute/cbor@2.3.3(@atcute/cid@2.4.1))(@atcute/cid@2.4.1)(typescript@5.9.3)': 1920 + dependencies: 1921 + '@atcute/identity': 2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3) 1922 + '@atcute/identity-resolver': 2.0.0(@atcute/identity@2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/lexicons@2.0.0)(typescript@5.9.3) 1923 + '@atcute/lexicon-doc': 3.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3) 1924 + '@atcute/lexicon-resolver': 1.0.0(@atcute/cbor@2.3.3(@atcute/cid@2.4.1))(@atcute/cid@2.4.1)(@atcute/identity-resolver@2.0.0(@atcute/identity@2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/identity@2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/lexicon-doc@3.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/lexicons@2.0.0)(typescript@5.9.3) 1925 + '@atcute/lexicons': 2.0.0 1926 + '@optique/core': 1.0.2 1927 + '@optique/run': 1.0.2 1928 + picocolors: 1.1.1 1929 + prettier: 3.8.3 1930 + valibot: 1.4.1(typescript@5.9.3) 1931 + transitivePeerDependencies: 1932 + - '@atcute/cbor' 1933 + - '@atcute/cid' 1934 + - typescript 1935 + 1936 + '@atcute/lexicon-doc@3.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3)': 1937 + dependencies: 1938 + '@atcute/identity': 2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3) 1939 + '@atcute/lexicons': 2.0.0 1940 + '@atcute/uint8array': 1.1.2 1941 + '@atcute/util-text': 1.3.1 1942 + valibot: 1.4.1(typescript@5.9.3) 1943 + transitivePeerDependencies: 1944 + - typescript 1945 + 1946 + '@atcute/lexicon-resolver@1.0.0(@atcute/cbor@2.3.3(@atcute/cid@2.4.1))(@atcute/cid@2.4.1)(@atcute/identity-resolver@2.0.0(@atcute/identity@2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/identity@2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/lexicon-doc@3.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/lexicons@2.0.0)(typescript@5.9.3)': 1947 + dependencies: 1948 + '@atcute/crypto': 2.4.1 1949 + '@atcute/identity': 2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3) 1950 + '@atcute/identity-resolver': 2.0.0(@atcute/identity@2.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3))(@atcute/lexicons@2.0.0)(typescript@5.9.3) 1951 + '@atcute/lexicon-doc': 3.0.0(@atcute/lexicons@2.0.0)(typescript@5.9.3) 1952 + '@atcute/lexicons': 2.0.0 1953 + '@atcute/repo': 1.0.0(@atcute/cbor@2.3.3(@atcute/cid@2.4.1))(@atcute/cid@2.4.1)(@atcute/lexicons@2.0.0) 1954 + '@atcute/util-fetch': 2.0.0(typescript@5.9.3) 1955 + valibot: 1.4.1(typescript@5.9.3) 1956 + transitivePeerDependencies: 1957 + - '@atcute/cbor' 1958 + - '@atcute/cid' 1959 + - typescript 1960 + 1823 1961 '@atcute/lexicons@2.0.0': 1824 1962 dependencies: 1825 1963 '@atcute/uint8array': 1.1.2 1826 1964 '@atcute/util-text': 1.3.1 1827 1965 '@standard-schema/spec': 1.1.0 1828 1966 esm-env: 1.2.2 1967 + 1968 + '@atcute/mst@1.0.1(@atcute/cbor@2.3.3(@atcute/cid@2.4.1))(@atcute/cid@2.4.1)': 1969 + dependencies: 1970 + '@atcute/cbor': 2.3.3(@atcute/cid@2.4.1) 1971 + '@atcute/cid': 2.4.1 1972 + '@atcute/uint8array': 1.1.2 1829 1973 1830 1974 '@atcute/multibase@1.2.0': 1831 1975 dependencies: ··· 1867 2011 transitivePeerDependencies: 1868 2012 - typescript 1869 2013 2014 + '@atcute/repo@1.0.0(@atcute/cbor@2.3.3(@atcute/cid@2.4.1))(@atcute/cid@2.4.1)(@atcute/lexicons@2.0.0)': 2015 + dependencies: 2016 + '@atcute/car': 6.0.0(@atcute/cbor@2.3.3(@atcute/cid@2.4.1))(@atcute/cid@2.4.1) 2017 + '@atcute/cbor': 2.3.3(@atcute/cid@2.4.1) 2018 + '@atcute/cid': 2.4.1 2019 + '@atcute/crypto': 2.4.1 2020 + '@atcute/lexicons': 2.0.0 2021 + '@atcute/mst': 1.0.1(@atcute/cbor@2.3.3(@atcute/cid@2.4.1))(@atcute/cid@2.4.1) 2022 + '@atcute/uint8array': 1.1.2 2023 + 1870 2024 '@atcute/tangled@2.0.5(@atcute/lexicons@2.0.0)': 1871 2025 dependencies: 1872 2026 '@atcute/atproto': 4.0.2(@atcute/lexicons@2.0.0) ··· 1883 2037 '@atcute/util-text@1.3.1': 1884 2038 dependencies: 1885 2039 unicode-segmenter: 0.14.5 2040 + 2041 + '@atcute/varint@2.0.0': {} 1886 2042 1887 2043 '@babel/code-frame@7.29.7': 1888 2044 dependencies: ··· 2116 2272 '@jridgewell/resolve-uri': 3.1.2 2117 2273 '@jridgewell/sourcemap-codec': 1.5.5 2118 2274 2275 + '@noble/secp256k1@3.1.0': {} 2276 + 2119 2277 '@nothing-but/utils@0.17.0': {} 2278 + 2279 + '@optique/core@1.0.2': {} 2280 + 2281 + '@optique/run@1.0.2': 2282 + dependencies: 2283 + '@optique/core': 1.0.2 2120 2284 2121 2285 '@rollup/rollup-android-arm-eabi@4.61.1': 2122 2286 optional: true ··· 3179 3343 nanoid: 3.3.12 3180 3344 picocolors: 1.1.1 3181 3345 source-map-js: 1.2.1 3346 + 3347 + prettier@3.8.3: {} 3182 3348 3183 3349 proxy-compare@3.0.1: {} 3184 3350
+9
app/lex.config.ts
··· 1 + import { defineLexiconConfig } from "@atcute/lex-cli"; 2 + 3 + export default defineLexiconConfig({ 4 + generate: { 5 + outdir: "src/generated/lexicons/", 6 + files: ["../lexicons/**/*.json"], 7 + imports: [], 8 + }, 9 + });
+6 -2
app/package.json
··· 4 4 "scripts": { 5 5 "start": "vite", 6 6 "dev": "vite", 7 - "build": "vite build", 8 - "serve": "vite preview" 7 + "build": "pnpm postinstall && vite build", 8 + "serve": "vite preview", 9 + "postinstall": "pnpm lexicon:generate", 10 + "lexicon:generate": "lex-cli generate" 9 11 }, 10 12 "dependencies": { 11 13 "@ark-ui/solid": "^5.37.1", 12 14 "@atcute/bluesky": "^4.0.6", 13 15 "@atcute/client": "^5.0.0", 14 16 "@atcute/identity-resolver": "^2.0.0", 17 + "@atcute/lex-cli": "^3.0.1", 18 + "@atcute/lexicon-doc": "^3.0.0", 15 19 "@atcute/lexicons": "^2.0.0", 16 20 "@atcute/oauth-browser-client": "^4.0.0", 17 21 "@atcute/tangled": "^2.0.5",
+3
app/tsconfig.json
··· 13 13 "module": "ESNext", 14 14 "moduleResolution": "bundler", 15 15 "noEmit": true, 16 + "paths": { 17 + "#/*": ["./src/generated/lexicons/types/*"], 18 + }, 16 19 17 20 // Type Checking & Safety 18 21 "strict": true,
+1
crates/knotty-knot/Cargo.toml
··· 6 6 7 7 [dependencies] 8 8 knotty-web = { path = "../knotty-web" } 9 + knotty-lexicons = { path = "../knotty-lexicons" } 9 10 10 11 axum.workspace = true 11 12 clap.workspace = true
+16
crates/knotty-lexgen/Cargo.toml
··· 1 + [package] 2 + name = "knotty-lexgen" 3 + edition = "2024" 4 + version.workspace = true 5 + description.workspace = true 6 + 7 + # Lexicon code generation for the Rust backend. 8 + # 9 + # Pinned to the same `jacquard-lexicon` minor as the `jacquard` runtime 10 + # (0.11): the generated code references `jacquard_common`/`jacquard_derive` 11 + # internals, so the generator and the runtime must agree on their API. The 12 + # published `jacquard-lexgen` binaries only exist for 0.12-beta, hence this 13 + # thin in-tree generator. 14 + [dependencies] 15 + jacquard-lexicon = "0.11" 16 + miette.workspace = true
+30
crates/knotty-lexicons/Cargo.toml
··· 1 + [package] 2 + name = "knotty-lexicons" 3 + edition = "2024" 4 + version.workspace = true 5 + description.workspace = true 6 + 7 + # Generated AT Protocol bindings for the `dev.drawbu.knotty.*` lexicons. 8 + # Do not edit `src/` by hand: regenerate with `cargo run -p xtask`. 9 + # Dependency set mirrors jacquard's own generated `jacquard-api` crate. 10 + 11 + [lib] 12 + name = "knotty_lexicons" 13 + path = "src/lib.rs" 14 + 15 + [features] 16 + default = ["std", "dev_drawbu"] 17 + std = ["serde/std", "jacquard-common/std"] 18 + dev_drawbu = [] 19 + 20 + [dependencies] 21 + jacquard-common = "0.11" 22 + jacquard-derive = "0.11" 23 + jacquard-lexicon = { version = "0.11", default-features = false } 24 + miette = "7.6" 25 + serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } 26 + thiserror = { version = "2.0", default-features = false } 27 + 28 + [lints.rust] 29 + non_snake_case = "allow" 30 + unused_imports = "allow"
+21
crates/knotty-lexgen/src/main.rs
··· 1 + //! Generates the Rust lexicon bindings in `crates/knotty-lexicons` from the 2 + //! shared `lexicons/` corpus. Run from the workspace root 3 + 4 + use std::path::Path; 5 + 6 + use jacquard_lexicon::codegen::{CodeGenerator, CodegenMode}; 7 + use jacquard_lexicon::corpus::LexiconCorpus; 8 + 9 + const INPUT: &str = "lexicons"; 10 + const OUTPUT: &str = "crates/knotty-lexicons/src"; 11 + 12 + fn main() -> miette::Result<()> { 13 + let corpus = LexiconCorpus::load_from_dir(INPUT)?; 14 + println!("loaded {} lexicon document(s) from {INPUT}", corpus.len()); 15 + 16 + let codegen = CodeGenerator::with_mode(&corpus, "crate", CodegenMode::Pretty); 17 + codegen.write_to_disk(Path::new(OUTPUT))?; 18 + println!("generated bindings into {OUTPUT}"); 19 + 20 + Ok(()) 21 + }
+45
crates/knotty-lexicons/src/builder_types.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + /// Marker type indicating a builder field has been set 7 + pub struct Set<T>(pub T); 8 + impl<T> Set<T> { 9 + /// Extract the inner value 10 + #[inline] 11 + pub fn into_inner(self) -> T { 12 + self.0 13 + } 14 + } 15 + 16 + /// Marker type indicating a builder field has not been set 17 + pub struct Unset; 18 + /// Trait indicating a builder field is set (has a value) 19 + 20 + #[jacquard_common::deps::codegen::rustversion::attr( 21 + since(1.78.0), 22 + diagnostic::on_unimplemented( 23 + message = "the field `{Self}` was not set, but this method requires it to be set", 24 + label = "the field `{Self}` was not set" 25 + ) 26 + )] 27 + pub trait IsSet: private::Sealed {} 28 + /// Trait indicating a builder field is unset (no value yet) 29 + 30 + #[jacquard_common::deps::codegen::rustversion::attr( 31 + since(1.78.0), 32 + diagnostic::on_unimplemented( 33 + message = "the field `{Self}` was already set, but this method requires it to be unset", 34 + label = "the field `{Self}` was already set" 35 + ) 36 + )] 37 + pub trait IsUnset: private::Sealed {} 38 + impl<T> IsSet for Set<T> {} 39 + impl IsUnset for Unset {} 40 + mod private { 41 + /// Sealed trait to prevent external implementations 42 + pub trait Sealed {} 43 + impl<T> Sealed for super::Set<T> {} 44 + impl Sealed for super::Unset {} 45 + }
+6
crates/knotty-lexicons/src/dev_drawbu.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + pub mod knotty;
+11
crates/knotty-lexicons/src/lib.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + extern crate alloc; 7 + pub mod builder_types; 8 + 9 + 10 + #[cfg(feature = "dev_drawbu")] 11 + pub mod dev_drawbu;
+1
app/src/generated/lexicons/index.ts
··· 1 + export * as DevDrawbuKnottyCreate from "./types/dev/drawbu/knotty/create.js";
+6
crates/knotty-lexicons/src/dev_drawbu/knotty.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + pub mod create;
+49
lexicons/dev/drawbu/knotty/create.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "dev.drawbu.knotty.create", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Create a new mirror of an upstream git repository.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["rkey", "name", "source"], 13 + "properties": { 14 + "rkey": { 15 + "type": "string", 16 + "description": "Rkey of the repository record." 17 + }, 18 + "name": { 19 + "type": "string", 20 + "description": "Name of the repository." 21 + }, 22 + "source": { 23 + "type": "string", 24 + "description": "Upstream URL to mirror from." 25 + }, 26 + "defaultBranch": { 27 + "type": "string", 28 + "description": "Branch to track. Defaults to the upstream HEAD." 29 + } 30 + } 31 + } 32 + }, 33 + "output": { 34 + "encoding": "application/json", 35 + "schema": { 36 + "type": "object", 37 + "required": ["repoDid"], 38 + "properties": { 39 + "repoDid": { 40 + "type": "string", 41 + "format": "did", 42 + "description": "Minted identity of the created mirror." 43 + } 44 + } 45 + } 46 + } 47 + } 48 + } 49 + }
+9 -2
crates/knotty-knot/src/xrpc/knotty/create.rs
··· 1 - use crate::{error::KnotError, state::KnotState}; 2 1 use axum::extract::State; 2 + use jacquard_axum::{ExtractXrpc, service_auth::ExtractServiceAuth}; 3 + use knotty_lexicons::dev_drawbu::knotty::create::CreateRequest; 4 + 5 + use crate::{error::KnotError, state::KnotState}; 3 6 4 7 #[axum::debug_handler(state = KnotState)] 5 - pub async fn handler(State(_state): State<KnotState>) -> Result<(), KnotError> { 8 + pub async fn handler( 9 + State(_state): State<KnotState>, 10 + ExtractServiceAuth(_auth): ExtractServiceAuth, 11 + ExtractXrpc(_req): ExtractXrpc<CreateRequest>, 12 + ) -> Result<(), KnotError> { 6 13 Err(KnotError::not_implemented()) 7 14 }
+3 -1
crates/knotty-knot/src/xrpc/knotty/mod.rs
··· 1 1 use axum::{Router, routing::post}; 2 + use jacquard_axum::IntoRouter; 3 + use knotty_lexicons::dev_drawbu::knotty::create::CreateRequest; 2 4 3 5 use crate::state::KnotState; 4 6 ··· 7 9 8 10 pub fn router() -> Router<KnotState> { 9 11 Router::new() 10 - .route("/xrpc/dev.drawbu.knotty.create", post(create::handler)) 12 + .merge(CreateRequest::into_router(create::handler)) 11 13 .route("/xrpc/dev.drawbu.knotty.list", post(list::handler)) 12 14 }
+73
crates/knotty-lexicons/src/dev_drawbu/knotty/create.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: dev.drawbu.knotty.create 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[allow(unused_imports)] 9 + use alloc::collections::BTreeMap; 10 + 11 + #[allow(unused_imports)] 12 + use core::marker::PhantomData; 13 + use jacquard_common::CowStr; 14 + use jacquard_common::types::string::Did; 15 + use jacquard_derive::{IntoStatic, lexicon}; 16 + use serde::{Serialize, Deserialize}; 17 + 18 + #[lexicon] 19 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic, Default)] 20 + #[serde(rename_all = "camelCase")] 21 + pub struct Create<'a> { 22 + ///Branch to track. Defaults to the upstream HEAD. 23 + #[serde(skip_serializing_if = "Option::is_none")] 24 + #[serde(borrow)] 25 + pub default_branch: Option<CowStr<'a>>, 26 + ///Name of the repository. 27 + #[serde(borrow)] 28 + pub name: CowStr<'a>, 29 + ///Rkey of the repository record. 30 + #[serde(borrow)] 31 + pub rkey: CowStr<'a>, 32 + ///Upstream URL to mirror from. 33 + #[serde(borrow)] 34 + pub source: CowStr<'a>, 35 + } 36 + 37 + 38 + #[lexicon] 39 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)] 40 + #[serde(rename_all = "camelCase")] 41 + pub struct CreateOutput<'a> { 42 + ///Minted identity of the created mirror. 43 + #[serde(borrow)] 44 + pub repo_did: Did<'a>, 45 + } 46 + 47 + /// Response type for dev.drawbu.knotty.create 48 + pub struct CreateResponse; 49 + impl jacquard_common::xrpc::XrpcResp for CreateResponse { 50 + const NSID: &'static str = "dev.drawbu.knotty.create"; 51 + const ENCODING: &'static str = "application/json"; 52 + type Output<'de> = CreateOutput<'de>; 53 + type Err<'de> = jacquard_common::xrpc::GenericError<'de>; 54 + } 55 + 56 + impl<'a> jacquard_common::xrpc::XrpcRequest for Create<'a> { 57 + const NSID: &'static str = "dev.drawbu.knotty.create"; 58 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure( 59 + "application/json", 60 + ); 61 + type Response = CreateResponse; 62 + } 63 + 64 + /// Endpoint type for dev.drawbu.knotty.create 65 + pub struct CreateRequest; 66 + impl jacquard_common::xrpc::XrpcEndpoint for CreateRequest { 67 + const PATH: &'static str = "/xrpc/dev.drawbu.knotty.create"; 68 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure( 69 + "application/json", 70 + ); 71 + type Request<'de> = Create<'de>; 72 + type Response = CreateResponse; 73 + }
+53
app/src/generated/lexicons/types/dev/drawbu/knotty/create.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + 5 + const _mainSchema = /*#__PURE__*/ v.procedure("dev.drawbu.knotty.create", { 6 + params: null, 7 + input: { 8 + type: "lex", 9 + schema: /*#__PURE__*/ v.object({ 10 + /** 11 + * Branch to track. Defaults to the upstream HEAD. 12 + */ 13 + defaultBranch: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 14 + /** 15 + * Name of the repository. 16 + */ 17 + name: /*#__PURE__*/ v.string(), 18 + /** 19 + * Rkey of the repository record. 20 + */ 21 + rkey: /*#__PURE__*/ v.string(), 22 + /** 23 + * Upstream URL to mirror from. 24 + */ 25 + source: /*#__PURE__*/ v.string(), 26 + }), 27 + }, 28 + output: { 29 + type: "lex", 30 + schema: /*#__PURE__*/ v.object({ 31 + /** 32 + * Minted identity of the created mirror. 33 + */ 34 + repoDid: /*#__PURE__*/ v.didString(), 35 + }), 36 + }, 37 + }); 38 + 39 + type main$schematype = typeof _mainSchema; 40 + 41 + export interface mainSchema extends main$schematype {} 42 + 43 + export const mainSchema = _mainSchema as mainSchema; 44 + 45 + export interface $params {} 46 + export interface $input extends v.InferXRPCBodyInput<mainSchema["input"]> {} 47 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 48 + 49 + declare module "@atcute/lexicons/ambient" { 50 + interface XRPCProcedures { 51 + "dev.drawbu.knotty.create": mainSchema; 52 + } 53 + }