the statusphere demo reworked into a vite/react app in a monorepo

lexgen an example lexicon

dholms 321f3435 a90cbaf4

+258 -10
+18
lexicons/status.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "example.lexicon.status", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "literal:self", 8 + "record": { 9 + "type": "object", 10 + "required": ["status", "updatedAt"], 11 + "properties": { 12 + "status": { "type": "string" }, 13 + "updatedAt": { "type": "string", "format": "datetime" } 14 + } 15 + } 16 + } 17 + } 18 + }
+10 -2
package.json
··· 10 10 "dev": "tsx watch --clear-screen=false src/index.ts | pino-pretty", 11 11 "build": "tsup", 12 12 "start": "node dist/index.js", 13 + "lexgen": "lex gen-server ./src/lexicon ./lexicons/*", 13 14 "clean": "rimraf dist coverage", 14 15 "lint": "biome check src/", 15 16 "lint:fix": "biome check src/ --fix", ··· 35 36 "pino-http": "^10.0.0" 36 37 }, 37 38 "devDependencies": { 39 + "@atproto/lex-cli": "^0.4.1", 38 40 "@biomejs/biome": "1.8.3", 39 41 "@types/better-sqlite3": "^7.6.11", 40 42 "@types/cors": "^2.8.17", ··· 50 52 "vitest": "^2.0.0" 51 53 }, 52 54 "lint-staged": { 53 - "*.{js,ts,cjs,mjs,d.cts,d.mts,json,jsonc}": ["biome check --apply --no-errors-on-unmatched"] 55 + "*.{js,ts,cjs,mjs,d.cts,d.mts,json,jsonc}": [ 56 + "biome check --apply --no-errors-on-unmatched" 57 + ] 54 58 }, 55 59 "tsup": { 56 - "entry": ["src", "!src/**/__tests__/**", "!src/**/*.test.*"], 60 + "entry": [ 61 + "src", 62 + "!src/**/__tests__/**", 63 + "!src/**/*.test.*" 64 + ], 57 65 "splitting": false, 58 66 "sourcemap": true, 59 67 "clean": true
+89 -8
pnpm-lock.yaml
··· 55 55 version: 10.2.0 56 56 57 57 devDependencies: 58 + '@atproto/lex-cli': 59 + specifier: ^0.4.1 60 + version: 0.4.1 58 61 '@biomejs/biome': 59 62 specifier: 1.8.3 60 63 version: 1.8.3 ··· 112 115 multiformats: 9.9.0 113 116 uint8arrays: 3.0.0 114 117 zod: 3.23.8 115 - dev: false 116 118 117 119 /@atproto/common@0.4.1: 118 120 resolution: {integrity: sha512-uL7kQIcBTbvkBDNfxMXL6lBH4fO2DQpHd2BryJxMtbw/4iEPKe9xBYApwECHhEIk9+zhhpTRZ15FJ3gxTXN82Q==} ··· 133 135 uint8arrays: 3.0.0 134 136 dev: false 135 137 138 + /@atproto/lex-cli@0.4.1: 139 + resolution: {integrity: sha512-QP9mE8MYzXR2ydhCBb/mtGqKZjqpffqcpZCr7JM4mFOZPvXV8k7OqVP1h+T94JB/tGcGPhB750S6tqUH9VRLVg==} 140 + hasBin: true 141 + dependencies: 142 + '@atproto/lexicon': 0.4.0 143 + '@atproto/syntax': 0.3.0 144 + chalk: 4.1.2 145 + commander: 9.5.0 146 + prettier: 3.3.3 147 + ts-morph: 16.0.0 148 + yesno: 0.4.0 149 + zod: 3.23.8 150 + dev: true 151 + 136 152 /@atproto/lexicon@0.4.0: 137 153 resolution: {integrity: sha512-RvCBKdSI4M8qWm5uTNz1z3R2yIvIhmOsMuleOj8YR6BwRD+QbtUBy3l+xQ7iXf4M5fdfJFxaUNa6Ty0iRwdKqQ==} 138 154 dependencies: ··· 141 157 iso-datestring-validator: 2.2.2 142 158 multiformats: 9.9.0 143 159 zod: 3.23.8 144 - dev: false 145 160 146 161 /@atproto/repo@0.4.1: 147 162 resolution: {integrity: sha512-DXv/cBwRcAM0KFb4SwafcQBONd0g31QUNLfjTri1bg5adCbX3bxxE4fCPpQM9Qc3+5lcCkTL/EniHW1j3UQjVA==} ··· 159 174 160 175 /@atproto/syntax@0.3.0: 161 176 resolution: {integrity: sha512-Weq0ZBxffGHDXHl9U7BQc2BFJi/e23AL+k+i5+D9hUq/bzT4yjGsrCejkjq0xt82xXDjmhhvQSZ0LqxyZ5woxA==} 162 - dev: false 163 177 164 178 /@atproto/xrpc-server@0.5.3: 165 179 resolution: {integrity: sha512-Gxe5dPDp7mj7E1JaK0yEwGuWot78/HjszHYakqleKp+IXlM+iZxH0N20O+x7b3g7itImuQ2LzH3Zk1jLB0yZjQ==} ··· 972 986 dev: true 973 987 optional: true 974 988 989 + /@ts-morph/common@0.17.0: 990 + resolution: {integrity: sha512-RMSSvSfs9kb0VzkvQ2NWobwnj7TxCA9vI/IjR9bDHqgAyVbu2T0DN4wiKVqomyDWqO7dPr/tErSfq7urQ1Q37g==} 991 + dependencies: 992 + fast-glob: 3.3.2 993 + minimatch: 5.1.6 994 + mkdirp: 1.0.4 995 + path-browserify: 1.0.1 996 + dev: true 997 + 975 998 /@types/better-sqlite3@7.6.11: 976 999 resolution: {integrity: sha512-i8KcD3PgGtGBLl3+mMYA8PdKkButvPyARxA7IQAd6qeslht13qxb1zzO8dRCtE7U3IoJS782zDBAeoKiM695kg==} 977 1000 dependencies: ··· 1330 1353 pathval: 2.0.0 1331 1354 dev: true 1332 1355 1356 + /chalk@4.1.2: 1357 + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 1358 + engines: {node: '>=10'} 1359 + dependencies: 1360 + ansi-styles: 4.3.0 1361 + supports-color: 7.2.0 1362 + dev: true 1363 + 1333 1364 /chalk@5.3.0: 1334 1365 resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} 1335 1366 engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} ··· 1374 1405 string-width: 7.2.0 1375 1406 dev: true 1376 1407 1408 + /code-block-writer@11.0.3: 1409 + resolution: {integrity: sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==} 1410 + dev: true 1411 + 1377 1412 /color-convert@2.0.1: 1378 1413 resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 1379 1414 engines: {node: '>=7.0.0'} ··· 1404 1439 /commander@4.1.1: 1405 1440 resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 1406 1441 engines: {node: '>= 6'} 1442 + dev: true 1443 + 1444 + /commander@9.5.0: 1445 + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} 1446 + engines: {node: ^12.20.0 || >=14} 1407 1447 dev: true 1408 1448 1409 1449 /component-emitter@1.3.1: ··· 1965 2005 1966 2006 /graphemer@1.4.0: 1967 2007 resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} 1968 - dev: false 2008 + 2009 + /has-flag@4.0.0: 2010 + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 2011 + engines: {node: '>=8'} 2012 + dev: true 1969 2013 1970 2014 /has-property-descriptors@1.0.2: 1971 2015 resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} ··· 2110 2154 2111 2155 /iso-datestring-validator@2.2.2: 2112 2156 resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==} 2113 - dev: false 2114 2157 2115 2158 /jackspeak@3.4.3: 2116 2159 resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} ··· 2278 2321 engines: {node: '>=10'} 2279 2322 dev: false 2280 2323 2324 + /minimatch@5.1.6: 2325 + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} 2326 + engines: {node: '>=10'} 2327 + dependencies: 2328 + brace-expansion: 2.0.1 2329 + dev: true 2330 + 2281 2331 /minimatch@9.0.5: 2282 2332 resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 2283 2333 engines: {node: '>=16 || 14 >=14.17'} ··· 2297 2347 resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} 2298 2348 dev: false 2299 2349 2350 + /mkdirp@1.0.4: 2351 + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} 2352 + engines: {node: '>=10'} 2353 + hasBin: true 2354 + dev: true 2355 + 2300 2356 /ms@2.0.0: 2301 2357 resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} 2302 2358 dev: false ··· 2311 2367 2312 2368 /multiformats@9.9.0: 2313 2369 resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} 2314 - dev: false 2315 2370 2316 2371 /mz@2.7.0: 2317 2372 resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} ··· 2424 2479 resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} 2425 2480 engines: {node: '>= 0.8'} 2426 2481 dev: false 2482 + 2483 + /path-browserify@1.0.1: 2484 + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} 2485 + dev: true 2427 2486 2428 2487 /path-key@3.1.1: 2429 2488 resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} ··· 2608 2667 tunnel-agent: 0.6.0 2609 2668 dev: false 2610 2669 2670 + /prettier@3.3.3: 2671 + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} 2672 + engines: {node: '>=14'} 2673 + hasBin: true 2674 + dev: true 2675 + 2611 2676 /process-warning@3.0.0: 2612 2677 resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} 2613 2678 dev: false ··· 3075 3140 - supports-color 3076 3141 dev: true 3077 3142 3143 + /supports-color@7.2.0: 3144 + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 3145 + engines: {node: '>=8'} 3146 + dependencies: 3147 + has-flag: 4.0.0 3148 + dev: true 3149 + 3078 3150 /tar-fs@2.1.1: 3079 3151 resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} 3080 3152 dependencies: ··· 3166 3238 resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 3167 3239 dev: true 3168 3240 3241 + /ts-morph@16.0.0: 3242 + resolution: {integrity: sha512-jGNF0GVpFj0orFw55LTsQxVYEUOCWBAbR5Ls7fTYE5pQsbW18ssTb/6UXx/GYAEjS+DQTp8VoTw0vqYMiaaQuw==} 3243 + dependencies: 3244 + '@ts-morph/common': 0.17.0 3245 + code-block-writer: 11.0.3 3246 + dev: true 3247 + 3169 3248 /tsconfck@3.1.1(typescript@5.5.4): 3170 3249 resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==} 3171 3250 engines: {node: ^18 || >=20} ··· 3261 3340 resolution: {integrity: sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==} 3262 3341 dependencies: 3263 3342 multiformats: 9.9.0 3264 - dev: false 3265 3343 3266 3344 /undici-types@6.13.0: 3267 3345 resolution: {integrity: sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==} ··· 3494 3572 hasBin: true 3495 3573 dev: true 3496 3574 3575 + /yesno@0.4.0: 3576 + resolution: {integrity: sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA==} 3577 + dev: true 3578 + 3497 3579 /zod@3.23.8: 3498 3580 resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} 3499 - dev: false
+69
src/lexicon/index.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { 5 + createServer as createXrpcServer, 6 + Server as XrpcServer, 7 + Options as XrpcOptions, 8 + AuthVerifier, 9 + StreamAuthVerifier, 10 + } from '@atproto/xrpc-server' 11 + import { schemas } from './lexicons' 12 + 13 + export function createServer(options?: XrpcOptions): Server { 14 + return new Server(options) 15 + } 16 + 17 + export class Server { 18 + xrpc: XrpcServer 19 + example: ExampleNS 20 + 21 + constructor(options?: XrpcOptions) { 22 + this.xrpc = createXrpcServer(schemas, options) 23 + this.example = new ExampleNS(this) 24 + } 25 + } 26 + 27 + export class ExampleNS { 28 + _server: Server 29 + lexicon: ExampleLexiconNS 30 + 31 + constructor(server: Server) { 32 + this._server = server 33 + this.lexicon = new ExampleLexiconNS(server) 34 + } 35 + } 36 + 37 + export class ExampleLexiconNS { 38 + _server: Server 39 + 40 + constructor(server: Server) { 41 + this._server = server 42 + } 43 + } 44 + 45 + type SharedRateLimitOpts<T> = { 46 + name: string 47 + calcKey?: (ctx: T) => string 48 + calcPoints?: (ctx: T) => number 49 + } 50 + type RouteRateLimitOpts<T> = { 51 + durationMs: number 52 + points: number 53 + calcKey?: (ctx: T) => string 54 + calcPoints?: (ctx: T) => number 55 + } 56 + type HandlerOpts = { blobLimit?: number } 57 + type HandlerRateLimitOpts<T> = SharedRateLimitOpts<T> | RouteRateLimitOpts<T> 58 + type ConfigOf<Auth, Handler, ReqCtx> = 59 + | Handler 60 + | { 61 + auth?: Auth 62 + opts?: HandlerOpts 63 + rateLimit?: HandlerRateLimitOpts<ReqCtx> | HandlerRateLimitOpts<ReqCtx>[] 64 + handler: Handler 65 + } 66 + type ExtractAuth<AV extends AuthVerifier | StreamAuthVerifier> = Extract< 67 + Awaited<ReturnType<AV>>, 68 + { credentials: unknown } 69 + >
+33
src/lexicon/lexicons.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { LexiconDoc, Lexicons } from '@atproto/lexicon' 5 + 6 + export const schemaDict = { 7 + ExampleLexiconStatus: { 8 + lexicon: 1, 9 + id: 'example.lexicon.status', 10 + defs: { 11 + main: { 12 + type: 'record', 13 + key: 'literal:self', 14 + record: { 15 + type: 'object', 16 + required: ['status', 'updatedAt'], 17 + properties: { 18 + status: { 19 + type: 'string', 20 + }, 21 + updatedAt: { 22 + type: 'string', 23 + format: 'datetime', 24 + }, 25 + }, 26 + }, 27 + }, 28 + }, 29 + }, 30 + } 31 + export const schemas: LexiconDoc[] = Object.values(schemaDict) as LexiconDoc[] 32 + export const lexicons: Lexicons = new Lexicons(schemas) 33 + export const ids = { ExampleLexiconStatus: 'example.lexicon.status' }
+26
src/lexicon/types/example/lexicon/status.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { lexicons } from '../../../lexicons' 6 + import { isObj, hasProp } from '../../../util' 7 + import { CID } from 'multiformats/cid' 8 + 9 + export interface Record { 10 + status: string 11 + updatedAt: string 12 + [k: string]: unknown 13 + } 14 + 15 + export function isRecord(v: unknown): v is Record { 16 + return ( 17 + isObj(v) && 18 + hasProp(v, '$type') && 19 + (v.$type === 'example.lexicon.status#main' || 20 + v.$type === 'example.lexicon.status') 21 + ) 22 + } 23 + 24 + export function validateRecord(v: unknown): ValidationResult { 25 + return lexicons.validate('example.lexicon.status#main', v) 26 + }
+13
src/lexicon/util.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + export function isObj(v: unknown): v is Record<string, unknown> { 5 + return typeof v === 'object' && v !== null 6 + } 7 + 8 + export function hasProp<K extends PropertyKey>( 9 + data: object, 10 + prop: K, 11 + ): data is Record<K, unknown> { 12 + return prop in data 13 + }