an atproto based link aggregator

Format code with Prettier

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+4163 -4450
+1 -3
.prettierrc
··· 3 3 "singleQuote": true, 4 4 "trailingComma": "none", 5 5 "printWidth": 100, 6 - "plugins": [ 7 - "prettier-plugin-svelte" 8 - ], 6 + "plugins": ["prettier-plugin-svelte"], 9 7 "overrides": [ 10 8 { 11 9 "files": "*.svelte",
+6 -6
.tangled/workflows/ci.yaml
··· 1 1 when: 2 - - event: ["push", "manual"] 3 - branch: ["main", "develop"] 4 - - event: ["pull_request"] 5 - branch: ["main"] 2 + - event: ['push', 'manual'] 3 + branch: ['main', 'develop'] 4 + - event: ['pull_request'] 5 + branch: ['main'] 6 6 7 7 engine: nixery 8 8 ··· 14 14 - gnused 15 15 16 16 environment: 17 - PLAYWRIGHT_BROWSERS_PATH: "0" 18 - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" 17 + PLAYWRIGHT_BROWSERS_PATH: '0' 18 + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' 19 19 CHROME_BIN: chromium 20 20 21 21 steps:
+8
CLAUDE.md
··· 37 37 - **Local DB** (`local.db`): Auth state, sessions, votes - webapp only 38 38 39 39 ### Data Flow 40 + 40 41 1. User submits post/comment → Webapp writes to ATProto (PDS) 41 42 2. Jetstream ingester picks up event → Writes to content DB (LiteFS primary) 42 43 3. LiteFS replicates to webapp machines → Content visible to users ··· 89 90 ## Key Files 90 91 91 92 ### Database & Schema 93 + 92 94 - `src/lib/server/db/content-schema.ts` - Content DB schema (posts, comments, accounts, reports) 93 95 - `src/lib/server/db/local-schema.ts` - Local DB schema (auth, votes) 94 96 - `src/lib/server/db/index.ts` - Database connections (contentDb, localDb) ··· 97 99 - `scripts/setup-fts.ts` - FTS5 full-text search setup script 98 100 99 101 ### Shared Query Utilities 102 + 100 103 - `src/lib/server/queries/posts.ts` - Post queries with comment counts 101 104 - `src/lib/server/queries/comments.ts` - Comment queries with hidden filtering 102 105 - `src/lib/server/search/queries.ts` - FTS search queries for posts/comments 103 106 104 107 ### Ingester 108 + 105 109 - `src/ingester/main.ts` - Standalone ingester entry point 106 110 - `src/ingester/handler.ts` - Jetstream event handler 107 111 108 112 ### Observability 113 + 109 114 - `src/instrumentation.server.ts` - OpenTelemetry tracing for SvelteKit 110 115 - `src/ingester/instrumentation.ts` - OpenTelemetry tracing for ingester 111 116 112 117 ### Deployment 118 + 113 119 - `fly.toml` - Webapp Fly.io config 114 120 - `fly.ingester.toml` - Ingester Fly.io config 115 121 - `litefs.yml` - LiteFS config for webapp (replica) ··· 117 123 - `.tangled/workflows/ci.yaml` - CI pipeline (lint, type check, tests) 118 124 119 125 ### Documentation 126 + 120 127 - `docs/implementation_plan.md` - Full implementation roadmap 121 128 - `lexicons/` - ATProto lexicon definitions (one.papili.post, one.papili.comment) 122 129 ··· 182 189 ## CI/CD 183 190 184 191 Pipeline runs on Tangled Spindle (`.tangled/workflows/ci.yaml`): 192 + 185 193 - Triggers on push to `main`/`develop` and PRs to `main` 186 194 - Runs: install deps → build lexicons → lint → type check → tests 187 195 - Uses nixpkgs for Node.js 22, pnpm, and Chromium (for browser tests)
+6 -8
eslint.config.js
··· 21 21 languageOptions: { 22 22 globals: { ...globals.browser, ...globals.node } 23 23 }, 24 - rules: { // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. 25 - // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors 26 - "no-undef": 'off' } 24 + rules: { 25 + // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. 26 + // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors 27 + 'no-undef': 'off' 28 + } 27 29 }, 28 30 { 29 - files: [ 30 - '**/*.svelte', 31 - '**/*.svelte.ts', 32 - '**/*.svelte.js' 33 - ], 31 + files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], 34 32 languageOptions: { 35 33 parserOptions: { 36 34 projectService: true,
+128 -128
lexicons.json
··· 1 1 { 2 - "version": 1, 3 - "lexicons": [ 4 - "app.bsky.actor.defs", 5 - "app.bsky.actor.getProfile", 6 - "app.bsky.actor.profile", 7 - "com.atproto.identity.resolveHandle", 8 - "com.atproto.repo.applyWrites", 9 - "com.atproto.repo.createRecord", 10 - "com.atproto.repo.deleteRecord", 11 - "com.atproto.repo.getRecord", 12 - "com.atproto.repo.listRecords", 13 - "com.atproto.repo.strongRef" 14 - ], 15 - "resolutions": { 16 - "app.bsky.actor.defs": { 17 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.defs", 18 - "cid": "bafyreihj55sr6x22fqtqre74cqmkflxwdtvx5f5wwuv26zzyki5hg2qo5m" 19 - }, 20 - "app.bsky.actor.getProfile": { 21 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.getProfile", 22 - "cid": "bafyreigrtosreva7e5m7bwbbfsmw77gkdnieizgxwpobw5iobuck3j54xa" 23 - }, 24 - "app.bsky.actor.profile": { 25 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.profile", 26 - "cid": "bafyreia6umzg3a6d7mjbow4p57tviey45muohklhgsvjoamcctoiusr4pe" 27 - }, 28 - "app.bsky.actor.status": { 29 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.status", 30 - "cid": "bafyreifdg4b64wohpwkh5lydc6tckvol2rspnpni6dec6recy2rhvlnz4a" 31 - }, 32 - "app.bsky.embed.defs": { 33 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.defs", 34 - "cid": "bafyreia42uud4qil67wknywzbxfyxc3b7woewsii54cakq2ould3ldetei" 35 - }, 36 - "app.bsky.embed.external": { 37 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.external", 38 - "cid": "bafyreiblxmpzgwg4fbr45b4xzts3h4k72k7cdnrxy2ub2w5d7mnwzznkwi" 39 - }, 40 - "app.bsky.embed.images": { 41 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.images", 42 - "cid": "bafyreifrntpx63uebiskpooozv6hji62swectq3pocw5h5gpkkqynmazdm" 43 - }, 44 - "app.bsky.embed.record": { 45 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.record", 46 - "cid": "bafyreigdtmu53blwxoygphg5zh5zpmlftz64c3jyqpv2yqpx3nrichkyla" 47 - }, 48 - "app.bsky.embed.recordWithMedia": { 49 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.recordWithMedia", 50 - "cid": "bafyreia7jrw2p73egm7vrunssgzeyj2rwmk3s4dymfhgzcavxjfaje3qfi" 51 - }, 52 - "app.bsky.embed.video": { 53 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.video", 54 - "cid": "bafyreib7cq67zkhasxlwomomskgnbuxvhzzde5brdkpv26fucbv3r7c5ue" 55 - }, 56 - "app.bsky.feed.defs": { 57 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.defs", 58 - "cid": "bafyreiadwvxawxifsnm7ae6l56aq23qs7ndih7npgs6pxmkoin7gi3k6pu" 59 - }, 60 - "app.bsky.feed.postgate": { 61 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.postgate", 62 - "cid": "bafyreiai5efexyluyptv5tbl6kqbqlnneczqzexcqnxmitmulyjfaftgva" 63 - }, 64 - "app.bsky.feed.threadgate": { 65 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.threadgate", 66 - "cid": "bafyreiht77wd6duduz4yqp62m6dwma5dy7gdihps4g2nd73acfzqlglvdi" 67 - }, 68 - "app.bsky.graph.defs": { 69 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.graph.defs", 70 - "cid": "bafyreierp3kbyh5oq7n5zvw3uqniiivbxlh4iibniyd7pf4qqmlhha6agm" 71 - }, 72 - "app.bsky.labeler.defs": { 73 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.labeler.defs", 74 - "cid": "bafyreicxx5i36v5dbqk5vvfzhnta5gajrvc544mnepux4wksrkid7mw3q4" 75 - }, 76 - "app.bsky.notification.defs": { 77 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.notification.defs", 78 - "cid": "bafyreickbpnayydlyfakliahgf23jjuesllh6qrslyofk5yz5xizjavhui" 79 - }, 80 - "app.bsky.richtext.facet": { 81 - "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.richtext.facet", 82 - "cid": "bafyreidg56eo7zynf6ihz4xb627vwoqf5idnevkmwp7sxc4tijg6xngbu4" 83 - }, 84 - "com.atproto.identity.resolveHandle": { 85 - "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.identity.resolveHandle", 86 - "cid": "bafyreigckmqtt3jrtzd7tvigjatnz6ajqyafs26h5pwcudwag2anedxnmu" 87 - }, 88 - "com.atproto.label.defs": { 89 - "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.label.defs", 90 - "cid": "bafyreig4hmnb2xkecyg4aaqfhr2rrcxxb3gsr4xks4rqb7rscrycalbrji" 91 - }, 92 - "com.atproto.moderation.defs": { 93 - "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.moderation.defs", 94 - "cid": "bafyreideawy4rlpgces2oebk5q4kpurbonhb5qtl4pes7dvxsc5osaiksy" 95 - }, 96 - "com.atproto.repo.applyWrites": { 97 - "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.applyWrites", 98 - "cid": "bafyreidwfwif7k5uncwqxhpxpxhvdkgedbvp6kqy6r4nozii433lwlajba" 99 - }, 100 - "com.atproto.repo.createRecord": { 101 - "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.createRecord", 102 - "cid": "bafyreihyvpmy2l4ou2v5etx25j5lbqty6tvna7gsxdqge76rbb7gltulei" 103 - }, 104 - "com.atproto.repo.defs": { 105 - "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.defs", 106 - "cid": "bafyreigmp5xfpkhnmwr3l5o626ued2gc2ouioykj2tpgqjqqajvbvm3wlm" 107 - }, 108 - "com.atproto.repo.deleteRecord": { 109 - "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.deleteRecord", 110 - "cid": "bafyreibwdxbyhuuljujl7g4ylwdso2x2ov6yv6nk3ext3sr7kjvvubkula" 111 - }, 112 - "com.atproto.repo.getRecord": { 113 - "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.getRecord", 114 - "cid": "bafyreihdnm3v25uaptt5vjvnpfsfgb6fbhxew2ye3l6q4ndm7xi5wpc4vy" 115 - }, 116 - "com.atproto.repo.listRecords": { 117 - "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.listRecords", 118 - "cid": "bafyreihv666jp6lxncj6amcgsu5qrvwq4rwmtc7xbxzndxqdwxamlsatee" 119 - }, 120 - "com.atproto.repo.strongRef": { 121 - "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.strongRef", 122 - "cid": "bafyreifrkdbnkvfjujntdaeigolnrjj3srrs53tfixjhmacclps72qlov4" 123 - }, 124 - "tools.ozone.report.defs": { 125 - "uri": "at://did:plc:33dt5kftu3jq2h5h4jjlqezt/com.atproto.lexicon.schema/tools.ozone.report.defs", 126 - "cid": "bafyreic3l2rmh2ugirt3jz372wcvy333m7t2ynlyzj2k54oshijs6lxdfu" 127 - } 128 - } 129 - } 2 + "version": 1, 3 + "lexicons": [ 4 + "app.bsky.actor.defs", 5 + "app.bsky.actor.getProfile", 6 + "app.bsky.actor.profile", 7 + "com.atproto.identity.resolveHandle", 8 + "com.atproto.repo.applyWrites", 9 + "com.atproto.repo.createRecord", 10 + "com.atproto.repo.deleteRecord", 11 + "com.atproto.repo.getRecord", 12 + "com.atproto.repo.listRecords", 13 + "com.atproto.repo.strongRef" 14 + ], 15 + "resolutions": { 16 + "app.bsky.actor.defs": { 17 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.defs", 18 + "cid": "bafyreihj55sr6x22fqtqre74cqmkflxwdtvx5f5wwuv26zzyki5hg2qo5m" 19 + }, 20 + "app.bsky.actor.getProfile": { 21 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.getProfile", 22 + "cid": "bafyreigrtosreva7e5m7bwbbfsmw77gkdnieizgxwpobw5iobuck3j54xa" 23 + }, 24 + "app.bsky.actor.profile": { 25 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.profile", 26 + "cid": "bafyreia6umzg3a6d7mjbow4p57tviey45muohklhgsvjoamcctoiusr4pe" 27 + }, 28 + "app.bsky.actor.status": { 29 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.status", 30 + "cid": "bafyreifdg4b64wohpwkh5lydc6tckvol2rspnpni6dec6recy2rhvlnz4a" 31 + }, 32 + "app.bsky.embed.defs": { 33 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.defs", 34 + "cid": "bafyreia42uud4qil67wknywzbxfyxc3b7woewsii54cakq2ould3ldetei" 35 + }, 36 + "app.bsky.embed.external": { 37 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.external", 38 + "cid": "bafyreiblxmpzgwg4fbr45b4xzts3h4k72k7cdnrxy2ub2w5d7mnwzznkwi" 39 + }, 40 + "app.bsky.embed.images": { 41 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.images", 42 + "cid": "bafyreifrntpx63uebiskpooozv6hji62swectq3pocw5h5gpkkqynmazdm" 43 + }, 44 + "app.bsky.embed.record": { 45 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.record", 46 + "cid": "bafyreigdtmu53blwxoygphg5zh5zpmlftz64c3jyqpv2yqpx3nrichkyla" 47 + }, 48 + "app.bsky.embed.recordWithMedia": { 49 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.recordWithMedia", 50 + "cid": "bafyreia7jrw2p73egm7vrunssgzeyj2rwmk3s4dymfhgzcavxjfaje3qfi" 51 + }, 52 + "app.bsky.embed.video": { 53 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.video", 54 + "cid": "bafyreib7cq67zkhasxlwomomskgnbuxvhzzde5brdkpv26fucbv3r7c5ue" 55 + }, 56 + "app.bsky.feed.defs": { 57 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.defs", 58 + "cid": "bafyreiadwvxawxifsnm7ae6l56aq23qs7ndih7npgs6pxmkoin7gi3k6pu" 59 + }, 60 + "app.bsky.feed.postgate": { 61 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.postgate", 62 + "cid": "bafyreiai5efexyluyptv5tbl6kqbqlnneczqzexcqnxmitmulyjfaftgva" 63 + }, 64 + "app.bsky.feed.threadgate": { 65 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.threadgate", 66 + "cid": "bafyreiht77wd6duduz4yqp62m6dwma5dy7gdihps4g2nd73acfzqlglvdi" 67 + }, 68 + "app.bsky.graph.defs": { 69 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.graph.defs", 70 + "cid": "bafyreierp3kbyh5oq7n5zvw3uqniiivbxlh4iibniyd7pf4qqmlhha6agm" 71 + }, 72 + "app.bsky.labeler.defs": { 73 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.labeler.defs", 74 + "cid": "bafyreicxx5i36v5dbqk5vvfzhnta5gajrvc544mnepux4wksrkid7mw3q4" 75 + }, 76 + "app.bsky.notification.defs": { 77 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.notification.defs", 78 + "cid": "bafyreickbpnayydlyfakliahgf23jjuesllh6qrslyofk5yz5xizjavhui" 79 + }, 80 + "app.bsky.richtext.facet": { 81 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.richtext.facet", 82 + "cid": "bafyreidg56eo7zynf6ihz4xb627vwoqf5idnevkmwp7sxc4tijg6xngbu4" 83 + }, 84 + "com.atproto.identity.resolveHandle": { 85 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.identity.resolveHandle", 86 + "cid": "bafyreigckmqtt3jrtzd7tvigjatnz6ajqyafs26h5pwcudwag2anedxnmu" 87 + }, 88 + "com.atproto.label.defs": { 89 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.label.defs", 90 + "cid": "bafyreig4hmnb2xkecyg4aaqfhr2rrcxxb3gsr4xks4rqb7rscrycalbrji" 91 + }, 92 + "com.atproto.moderation.defs": { 93 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.moderation.defs", 94 + "cid": "bafyreideawy4rlpgces2oebk5q4kpurbonhb5qtl4pes7dvxsc5osaiksy" 95 + }, 96 + "com.atproto.repo.applyWrites": { 97 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.applyWrites", 98 + "cid": "bafyreidwfwif7k5uncwqxhpxpxhvdkgedbvp6kqy6r4nozii433lwlajba" 99 + }, 100 + "com.atproto.repo.createRecord": { 101 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.createRecord", 102 + "cid": "bafyreihyvpmy2l4ou2v5etx25j5lbqty6tvna7gsxdqge76rbb7gltulei" 103 + }, 104 + "com.atproto.repo.defs": { 105 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.defs", 106 + "cid": "bafyreigmp5xfpkhnmwr3l5o626ued2gc2ouioykj2tpgqjqqajvbvm3wlm" 107 + }, 108 + "com.atproto.repo.deleteRecord": { 109 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.deleteRecord", 110 + "cid": "bafyreibwdxbyhuuljujl7g4ylwdso2x2ov6yv6nk3ext3sr7kjvvubkula" 111 + }, 112 + "com.atproto.repo.getRecord": { 113 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.getRecord", 114 + "cid": "bafyreihdnm3v25uaptt5vjvnpfsfgb6fbhxew2ye3l6q4ndm7xi5wpc4vy" 115 + }, 116 + "com.atproto.repo.listRecords": { 117 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.listRecords", 118 + "cid": "bafyreihv666jp6lxncj6amcgsu5qrvwq4rwmtc7xbxzndxqdwxamlsatee" 119 + }, 120 + "com.atproto.repo.strongRef": { 121 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.strongRef", 122 + "cid": "bafyreifrkdbnkvfjujntdaeigolnrjj3srrs53tfixjhmacclps72qlov4" 123 + }, 124 + "tools.ozone.report.defs": { 125 + "uri": "at://did:plc:33dt5kftu3jq2h5h4jjlqezt/com.atproto.lexicon.schema/tools.ozone.report.defs", 126 + "cid": "bafyreic3l2rmh2ugirt3jz372wcvy333m7t2ynlyzj2k54oshijs6lxdfu" 127 + } 128 + } 129 + }
+776 -882
lexicons/app/bsky/actor/defs.json
··· 1 1 { 2 - "id": "app.bsky.actor.defs", 3 - "defs": { 4 - "nux": { 5 - "type": "object", 6 - "required": [ 7 - "id", 8 - "completed" 9 - ], 10 - "properties": { 11 - "id": { 12 - "type": "string", 13 - "maxLength": 100 14 - }, 15 - "data": { 16 - "type": "string", 17 - "maxLength": 3000, 18 - "description": "Arbitrary data for the NUX. The structure is defined by the NUX itself. Limited to 300 characters.", 19 - "maxGraphemes": 300 20 - }, 21 - "completed": { 22 - "type": "boolean", 23 - "default": false 24 - }, 25 - "expiresAt": { 26 - "type": "string", 27 - "format": "datetime", 28 - "description": "The date and time at which the NUX will expire and should be considered completed." 29 - } 30 - }, 31 - "description": "A new user experiences (NUX) storage object" 32 - }, 33 - "mutedWord": { 34 - "type": "object", 35 - "required": [ 36 - "value", 37 - "targets" 38 - ], 39 - "properties": { 40 - "id": { 41 - "type": "string" 42 - }, 43 - "value": { 44 - "type": "string", 45 - "maxLength": 10000, 46 - "description": "The muted word itself.", 47 - "maxGraphemes": 1000 48 - }, 49 - "targets": { 50 - "type": "array", 51 - "items": { 52 - "ref": "app.bsky.actor.defs#mutedWordTarget", 53 - "type": "ref" 54 - }, 55 - "description": "The intended targets of the muted word." 56 - }, 57 - "expiresAt": { 58 - "type": "string", 59 - "format": "datetime", 60 - "description": "The date and time at which the muted word will expire and no longer be applied." 61 - }, 62 - "actorTarget": { 63 - "type": "string", 64 - "default": "all", 65 - "description": "Groups of users to apply the muted word to. If undefined, applies to all users.", 66 - "knownValues": [ 67 - "all", 68 - "exclude-following" 69 - ] 70 - } 71 - }, 72 - "description": "A word that the account owner has muted." 73 - }, 74 - "savedFeed": { 75 - "type": "object", 76 - "required": [ 77 - "id", 78 - "type", 79 - "value", 80 - "pinned" 81 - ], 82 - "properties": { 83 - "id": { 84 - "type": "string" 85 - }, 86 - "type": { 87 - "type": "string", 88 - "knownValues": [ 89 - "feed", 90 - "list", 91 - "timeline" 92 - ] 93 - }, 94 - "value": { 95 - "type": "string" 96 - }, 97 - "pinned": { 98 - "type": "boolean" 99 - } 100 - } 101 - }, 102 - "statusView": { 103 - "type": "object", 104 - "required": [ 105 - "status", 106 - "record" 107 - ], 108 - "properties": { 109 - "embed": { 110 - "refs": [ 111 - "app.bsky.embed.external#view" 112 - ], 113 - "type": "union", 114 - "description": "An optional embed associated with the status." 115 - }, 116 - "record": { 117 - "type": "unknown" 118 - }, 119 - "status": { 120 - "type": "string", 121 - "description": "The status for the account.", 122 - "knownValues": [ 123 - "app.bsky.actor.status#live" 124 - ] 125 - }, 126 - "isActive": { 127 - "type": "boolean", 128 - "description": "True if the status is not expired, false if it is expired. Only present if expiration was set." 129 - }, 130 - "expiresAt": { 131 - "type": "string", 132 - "format": "datetime", 133 - "description": "The date when this status will expire. The application might choose to no longer return the status after expiration." 134 - } 135 - } 136 - }, 137 - "preferences": { 138 - "type": "array", 139 - "items": { 140 - "refs": [ 141 - "#adultContentPref", 142 - "#contentLabelPref", 143 - "#savedFeedsPref", 144 - "#savedFeedsPrefV2", 145 - "#personalDetailsPref", 146 - "#feedViewPref", 147 - "#threadViewPref", 148 - "#interestsPref", 149 - "#mutedWordsPref", 150 - "#hiddenPostsPref", 151 - "#bskyAppStatePref", 152 - "#labelersPref", 153 - "#postInteractionSettingsPref", 154 - "#verificationPrefs" 155 - ], 156 - "type": "union" 157 - } 158 - }, 159 - "profileView": { 160 - "type": "object", 161 - "required": [ 162 - "did", 163 - "handle" 164 - ], 165 - "properties": { 166 - "did": { 167 - "type": "string", 168 - "format": "did" 169 - }, 170 - "debug": { 171 - "type": "unknown", 172 - "description": "Debug information for internal development" 173 - }, 174 - "avatar": { 175 - "type": "string", 176 - "format": "uri" 177 - }, 178 - "handle": { 179 - "type": "string", 180 - "format": "handle" 181 - }, 182 - "labels": { 183 - "type": "array", 184 - "items": { 185 - "ref": "com.atproto.label.defs#label", 186 - "type": "ref" 187 - } 188 - }, 189 - "status": { 190 - "ref": "#statusView", 191 - "type": "ref" 192 - }, 193 - "viewer": { 194 - "ref": "#viewerState", 195 - "type": "ref" 196 - }, 197 - "pronouns": { 198 - "type": "string" 199 - }, 200 - "createdAt": { 201 - "type": "string", 202 - "format": "datetime" 203 - }, 204 - "indexedAt": { 205 - "type": "string", 206 - "format": "datetime" 207 - }, 208 - "associated": { 209 - "ref": "#profileAssociated", 210 - "type": "ref" 211 - }, 212 - "description": { 213 - "type": "string", 214 - "maxLength": 2560, 215 - "maxGraphemes": 256 216 - }, 217 - "displayName": { 218 - "type": "string", 219 - "maxLength": 640, 220 - "maxGraphemes": 64 221 - }, 222 - "verification": { 223 - "ref": "#verificationState", 224 - "type": "ref" 225 - } 226 - } 227 - }, 228 - "viewerState": { 229 - "type": "object", 230 - "properties": { 231 - "muted": { 232 - "type": "boolean" 233 - }, 234 - "blocking": { 235 - "type": "string", 236 - "format": "at-uri" 237 - }, 238 - "blockedBy": { 239 - "type": "boolean" 240 - }, 241 - "following": { 242 - "type": "string", 243 - "format": "at-uri" 244 - }, 245 - "followedBy": { 246 - "type": "string", 247 - "format": "at-uri" 248 - }, 249 - "mutedByList": { 250 - "ref": "app.bsky.graph.defs#listViewBasic", 251 - "type": "ref" 252 - }, 253 - "blockingByList": { 254 - "ref": "app.bsky.graph.defs#listViewBasic", 255 - "type": "ref" 256 - }, 257 - "knownFollowers": { 258 - "ref": "#knownFollowers", 259 - "type": "ref", 260 - "description": "This property is present only in selected cases, as an optimization." 261 - }, 262 - "activitySubscription": { 263 - "ref": "app.bsky.notification.defs#activitySubscription", 264 - "type": "ref", 265 - "description": "This property is present only in selected cases, as an optimization." 266 - } 267 - }, 268 - "description": "Metadata about the requesting account's relationship with the subject account. Only has meaningful content for authed requests." 269 - }, 270 - "feedViewPref": { 271 - "type": "object", 272 - "required": [ 273 - "feed" 274 - ], 275 - "properties": { 276 - "feed": { 277 - "type": "string", 278 - "description": "The URI of the feed, or an identifier which describes the feed." 279 - }, 280 - "hideReplies": { 281 - "type": "boolean", 282 - "description": "Hide replies in the feed." 283 - }, 284 - "hideReposts": { 285 - "type": "boolean", 286 - "description": "Hide reposts in the feed." 287 - }, 288 - "hideQuotePosts": { 289 - "type": "boolean", 290 - "description": "Hide quote posts in the feed." 291 - }, 292 - "hideRepliesByLikeCount": { 293 - "type": "integer", 294 - "description": "Hide replies in the feed if they do not have this number of likes." 295 - }, 296 - "hideRepliesByUnfollowed": { 297 - "type": "boolean", 298 - "default": true, 299 - "description": "Hide replies in the feed if they are not by followed users." 300 - } 301 - } 302 - }, 303 - "labelersPref": { 304 - "type": "object", 305 - "required": [ 306 - "labelers" 307 - ], 308 - "properties": { 309 - "labelers": { 310 - "type": "array", 311 - "items": { 312 - "ref": "#labelerPrefItem", 313 - "type": "ref" 314 - } 315 - } 316 - } 317 - }, 318 - "interestsPref": { 319 - "type": "object", 320 - "required": [ 321 - "tags" 322 - ], 323 - "properties": { 324 - "tags": { 325 - "type": "array", 326 - "items": { 327 - "type": "string", 328 - "maxLength": 640, 329 - "maxGraphemes": 64 330 - }, 331 - "maxLength": 100, 332 - "description": "A list of tags which describe the account owner's interests gathered during onboarding." 333 - } 334 - } 335 - }, 336 - "knownFollowers": { 337 - "type": "object", 338 - "required": [ 339 - "count", 340 - "followers" 341 - ], 342 - "properties": { 343 - "count": { 344 - "type": "integer" 345 - }, 346 - "followers": { 347 - "type": "array", 348 - "items": { 349 - "ref": "#profileViewBasic", 350 - "type": "ref" 351 - }, 352 - "maxLength": 5, 353 - "minLength": 0 354 - } 355 - }, 356 - "description": "The subject's followers whom you also follow" 357 - }, 358 - "mutedWordsPref": { 359 - "type": "object", 360 - "required": [ 361 - "items" 362 - ], 363 - "properties": { 364 - "items": { 365 - "type": "array", 366 - "items": { 367 - "ref": "app.bsky.actor.defs#mutedWord", 368 - "type": "ref" 369 - }, 370 - "description": "A list of words the account owner has muted." 371 - } 372 - } 373 - }, 374 - "savedFeedsPref": { 375 - "type": "object", 376 - "required": [ 377 - "pinned", 378 - "saved" 379 - ], 380 - "properties": { 381 - "saved": { 382 - "type": "array", 383 - "items": { 384 - "type": "string", 385 - "format": "at-uri" 386 - } 387 - }, 388 - "pinned": { 389 - "type": "array", 390 - "items": { 391 - "type": "string", 392 - "format": "at-uri" 393 - } 394 - }, 395 - "timelineIndex": { 396 - "type": "integer" 397 - } 398 - } 399 - }, 400 - "threadViewPref": { 401 - "type": "object", 402 - "properties": { 403 - "sort": { 404 - "type": "string", 405 - "description": "Sorting mode for threads.", 406 - "knownValues": [ 407 - "oldest", 408 - "newest", 409 - "most-likes", 410 - "random", 411 - "hotness" 412 - ] 413 - } 414 - } 415 - }, 416 - "hiddenPostsPref": { 417 - "type": "object", 418 - "required": [ 419 - "items" 420 - ], 421 - "properties": { 422 - "items": { 423 - "type": "array", 424 - "items": { 425 - "type": "string", 426 - "format": "at-uri" 427 - }, 428 - "description": "A list of URIs of posts the account owner has hidden." 429 - } 430 - } 431 - }, 432 - "labelerPrefItem": { 433 - "type": "object", 434 - "required": [ 435 - "did" 436 - ], 437 - "properties": { 438 - "did": { 439 - "type": "string", 440 - "format": "did" 441 - } 442 - } 443 - }, 444 - "mutedWordTarget": { 445 - "type": "string", 446 - "maxLength": 640, 447 - "knownValues": [ 448 - "content", 449 - "tag" 450 - ], 451 - "maxGraphemes": 64 452 - }, 453 - "adultContentPref": { 454 - "type": "object", 455 - "required": [ 456 - "enabled" 457 - ], 458 - "properties": { 459 - "enabled": { 460 - "type": "boolean", 461 - "default": false 462 - } 463 - } 464 - }, 465 - "bskyAppStatePref": { 466 - "type": "object", 467 - "properties": { 468 - "nuxs": { 469 - "type": "array", 470 - "items": { 471 - "ref": "app.bsky.actor.defs#nux", 472 - "type": "ref" 473 - }, 474 - "maxLength": 100, 475 - "description": "Storage for NUXs the user has encountered." 476 - }, 477 - "queuedNudges": { 478 - "type": "array", 479 - "items": { 480 - "type": "string", 481 - "maxLength": 100 482 - }, 483 - "maxLength": 1000, 484 - "description": "An array of tokens which identify nudges (modals, popups, tours, highlight dots) that should be shown to the user." 485 - }, 486 - "activeProgressGuide": { 487 - "ref": "#bskyAppProgressGuide", 488 - "type": "ref" 489 - } 490 - }, 491 - "description": "A grab bag of state that's specific to the bsky.app program. Third-party apps shouldn't use this." 492 - }, 493 - "contentLabelPref": { 494 - "type": "object", 495 - "required": [ 496 - "label", 497 - "visibility" 498 - ], 499 - "properties": { 500 - "label": { 501 - "type": "string" 502 - }, 503 - "labelerDid": { 504 - "type": "string", 505 - "format": "did", 506 - "description": "Which labeler does this preference apply to? If undefined, applies globally." 507 - }, 508 - "visibility": { 509 - "type": "string", 510 - "knownValues": [ 511 - "ignore", 512 - "show", 513 - "warn", 514 - "hide" 515 - ] 516 - } 517 - } 518 - }, 519 - "profileViewBasic": { 520 - "type": "object", 521 - "required": [ 522 - "did", 523 - "handle" 524 - ], 525 - "properties": { 526 - "did": { 527 - "type": "string", 528 - "format": "did" 529 - }, 530 - "debug": { 531 - "type": "unknown", 532 - "description": "Debug information for internal development" 533 - }, 534 - "avatar": { 535 - "type": "string", 536 - "format": "uri" 537 - }, 538 - "handle": { 539 - "type": "string", 540 - "format": "handle" 541 - }, 542 - "labels": { 543 - "type": "array", 544 - "items": { 545 - "ref": "com.atproto.label.defs#label", 546 - "type": "ref" 547 - } 548 - }, 549 - "status": { 550 - "ref": "#statusView", 551 - "type": "ref" 552 - }, 553 - "viewer": { 554 - "ref": "#viewerState", 555 - "type": "ref" 556 - }, 557 - "pronouns": { 558 - "type": "string" 559 - }, 560 - "createdAt": { 561 - "type": "string", 562 - "format": "datetime" 563 - }, 564 - "associated": { 565 - "ref": "#profileAssociated", 566 - "type": "ref" 567 - }, 568 - "displayName": { 569 - "type": "string", 570 - "maxLength": 640, 571 - "maxGraphemes": 64 572 - }, 573 - "verification": { 574 - "ref": "#verificationState", 575 - "type": "ref" 576 - } 577 - } 578 - }, 579 - "savedFeedsPrefV2": { 580 - "type": "object", 581 - "required": [ 582 - "items" 583 - ], 584 - "properties": { 585 - "items": { 586 - "type": "array", 587 - "items": { 588 - "ref": "app.bsky.actor.defs#savedFeed", 589 - "type": "ref" 590 - } 591 - } 592 - } 593 - }, 594 - "verificationView": { 595 - "type": "object", 596 - "required": [ 597 - "issuer", 598 - "uri", 599 - "isValid", 600 - "createdAt" 601 - ], 602 - "properties": { 603 - "uri": { 604 - "type": "string", 605 - "format": "at-uri", 606 - "description": "The AT-URI of the verification record." 607 - }, 608 - "issuer": { 609 - "type": "string", 610 - "format": "did", 611 - "description": "The user who issued this verification." 612 - }, 613 - "isValid": { 614 - "type": "boolean", 615 - "description": "True if the verification passes validation, otherwise false." 616 - }, 617 - "createdAt": { 618 - "type": "string", 619 - "format": "datetime", 620 - "description": "Timestamp when the verification was created." 621 - } 622 - }, 623 - "description": "An individual verification for an associated subject." 624 - }, 625 - "profileAssociated": { 626 - "type": "object", 627 - "properties": { 628 - "chat": { 629 - "ref": "#profileAssociatedChat", 630 - "type": "ref" 631 - }, 632 - "lists": { 633 - "type": "integer" 634 - }, 635 - "labeler": { 636 - "type": "boolean" 637 - }, 638 - "feedgens": { 639 - "type": "integer" 640 - }, 641 - "starterPacks": { 642 - "type": "integer" 643 - }, 644 - "activitySubscription": { 645 - "ref": "#profileAssociatedActivitySubscription", 646 - "type": "ref" 647 - } 648 - } 649 - }, 650 - "verificationPrefs": { 651 - "type": "object", 652 - "required": [], 653 - "properties": { 654 - "hideBadges": { 655 - "type": "boolean", 656 - "default": false, 657 - "description": "Hide the blue check badges for verified accounts and trusted verifiers." 658 - } 659 - }, 660 - "description": "Preferences for how verified accounts appear in the app." 661 - }, 662 - "verificationState": { 663 - "type": "object", 664 - "required": [ 665 - "verifications", 666 - "verifiedStatus", 667 - "trustedVerifierStatus" 668 - ], 669 - "properties": { 670 - "verifications": { 671 - "type": "array", 672 - "items": { 673 - "ref": "#verificationView", 674 - "type": "ref" 675 - }, 676 - "description": "All verifications issued by trusted verifiers on behalf of this user. Verifications by untrusted verifiers are not included." 677 - }, 678 - "verifiedStatus": { 679 - "type": "string", 680 - "description": "The user's status as a verified account.", 681 - "knownValues": [ 682 - "valid", 683 - "invalid", 684 - "none" 685 - ] 686 - }, 687 - "trustedVerifierStatus": { 688 - "type": "string", 689 - "description": "The user's status as a trusted verifier.", 690 - "knownValues": [ 691 - "valid", 692 - "invalid", 693 - "none" 694 - ] 695 - } 696 - }, 697 - "description": "Represents the verification information about the user this object is attached to." 698 - }, 699 - "personalDetailsPref": { 700 - "type": "object", 701 - "properties": { 702 - "birthDate": { 703 - "type": "string", 704 - "format": "datetime", 705 - "description": "The birth date of account owner." 706 - } 707 - } 708 - }, 709 - "profileViewDetailed": { 710 - "type": "object", 711 - "required": [ 712 - "did", 713 - "handle" 714 - ], 715 - "properties": { 716 - "did": { 717 - "type": "string", 718 - "format": "did" 719 - }, 720 - "debug": { 721 - "type": "unknown", 722 - "description": "Debug information for internal development" 723 - }, 724 - "avatar": { 725 - "type": "string", 726 - "format": "uri" 727 - }, 728 - "banner": { 729 - "type": "string", 730 - "format": "uri" 731 - }, 732 - "handle": { 733 - "type": "string", 734 - "format": "handle" 735 - }, 736 - "labels": { 737 - "type": "array", 738 - "items": { 739 - "ref": "com.atproto.label.defs#label", 740 - "type": "ref" 741 - } 742 - }, 743 - "status": { 744 - "ref": "#statusView", 745 - "type": "ref" 746 - }, 747 - "viewer": { 748 - "ref": "#viewerState", 749 - "type": "ref" 750 - }, 751 - "website": { 752 - "type": "string", 753 - "format": "uri" 754 - }, 755 - "pronouns": { 756 - "type": "string" 757 - }, 758 - "createdAt": { 759 - "type": "string", 760 - "format": "datetime" 761 - }, 762 - "indexedAt": { 763 - "type": "string", 764 - "format": "datetime" 765 - }, 766 - "associated": { 767 - "ref": "#profileAssociated", 768 - "type": "ref" 769 - }, 770 - "pinnedPost": { 771 - "ref": "com.atproto.repo.strongRef", 772 - "type": "ref" 773 - }, 774 - "postsCount": { 775 - "type": "integer" 776 - }, 777 - "description": { 778 - "type": "string", 779 - "maxLength": 2560, 780 - "maxGraphemes": 256 781 - }, 782 - "displayName": { 783 - "type": "string", 784 - "maxLength": 640, 785 - "maxGraphemes": 64 786 - }, 787 - "followsCount": { 788 - "type": "integer" 789 - }, 790 - "verification": { 791 - "ref": "#verificationState", 792 - "type": "ref" 793 - }, 794 - "followersCount": { 795 - "type": "integer" 796 - }, 797 - "joinedViaStarterPack": { 798 - "ref": "app.bsky.graph.defs#starterPackViewBasic", 799 - "type": "ref" 800 - } 801 - } 802 - }, 803 - "bskyAppProgressGuide": { 804 - "type": "object", 805 - "required": [ 806 - "guide" 807 - ], 808 - "properties": { 809 - "guide": { 810 - "type": "string", 811 - "maxLength": 100 812 - } 813 - }, 814 - "description": "If set, an active progress guide. Once completed, can be set to undefined. Should have unspecced fields tracking progress." 815 - }, 816 - "profileAssociatedChat": { 817 - "type": "object", 818 - "required": [ 819 - "allowIncoming" 820 - ], 821 - "properties": { 822 - "allowIncoming": { 823 - "type": "string", 824 - "knownValues": [ 825 - "all", 826 - "none", 827 - "following" 828 - ] 829 - } 830 - } 831 - }, 832 - "postInteractionSettingsPref": { 833 - "type": "object", 834 - "required": [], 835 - "properties": { 836 - "threadgateAllowRules": { 837 - "type": "array", 838 - "items": { 839 - "refs": [ 840 - "app.bsky.feed.threadgate#mentionRule", 841 - "app.bsky.feed.threadgate#followerRule", 842 - "app.bsky.feed.threadgate#followingRule", 843 - "app.bsky.feed.threadgate#listRule" 844 - ], 845 - "type": "union" 846 - }, 847 - "maxLength": 5, 848 - "description": "Matches threadgate record. List of rules defining who can reply to this users posts. If value is an empty array, no one can reply. If value is undefined, anyone can reply." 849 - }, 850 - "postgateEmbeddingRules": { 851 - "type": "array", 852 - "items": { 853 - "refs": [ 854 - "app.bsky.feed.postgate#disableRule" 855 - ], 856 - "type": "union" 857 - }, 858 - "maxLength": 5, 859 - "description": "Matches postgate record. List of rules defining who can embed this users posts. If value is an empty array or is undefined, no particular rules apply and anyone can embed." 860 - } 861 - }, 862 - "description": "Default post interaction settings for the account. These values should be applied as default values when creating new posts. These refs should mirror the threadgate and postgate records exactly." 863 - }, 864 - "profileAssociatedActivitySubscription": { 865 - "type": "object", 866 - "required": [ 867 - "allowSubscriptions" 868 - ], 869 - "properties": { 870 - "allowSubscriptions": { 871 - "type": "string", 872 - "knownValues": [ 873 - "followers", 874 - "mutuals", 875 - "none" 876 - ] 877 - } 878 - } 879 - } 880 - }, 881 - "$type": "com.atproto.lexicon.schema", 882 - "lexicon": 1 883 - } 2 + "id": "app.bsky.actor.defs", 3 + "defs": { 4 + "nux": { 5 + "type": "object", 6 + "required": ["id", "completed"], 7 + "properties": { 8 + "id": { 9 + "type": "string", 10 + "maxLength": 100 11 + }, 12 + "data": { 13 + "type": "string", 14 + "maxLength": 3000, 15 + "description": "Arbitrary data for the NUX. The structure is defined by the NUX itself. Limited to 300 characters.", 16 + "maxGraphemes": 300 17 + }, 18 + "completed": { 19 + "type": "boolean", 20 + "default": false 21 + }, 22 + "expiresAt": { 23 + "type": "string", 24 + "format": "datetime", 25 + "description": "The date and time at which the NUX will expire and should be considered completed." 26 + } 27 + }, 28 + "description": "A new user experiences (NUX) storage object" 29 + }, 30 + "mutedWord": { 31 + "type": "object", 32 + "required": ["value", "targets"], 33 + "properties": { 34 + "id": { 35 + "type": "string" 36 + }, 37 + "value": { 38 + "type": "string", 39 + "maxLength": 10000, 40 + "description": "The muted word itself.", 41 + "maxGraphemes": 1000 42 + }, 43 + "targets": { 44 + "type": "array", 45 + "items": { 46 + "ref": "app.bsky.actor.defs#mutedWordTarget", 47 + "type": "ref" 48 + }, 49 + "description": "The intended targets of the muted word." 50 + }, 51 + "expiresAt": { 52 + "type": "string", 53 + "format": "datetime", 54 + "description": "The date and time at which the muted word will expire and no longer be applied." 55 + }, 56 + "actorTarget": { 57 + "type": "string", 58 + "default": "all", 59 + "description": "Groups of users to apply the muted word to. If undefined, applies to all users.", 60 + "knownValues": ["all", "exclude-following"] 61 + } 62 + }, 63 + "description": "A word that the account owner has muted." 64 + }, 65 + "savedFeed": { 66 + "type": "object", 67 + "required": ["id", "type", "value", "pinned"], 68 + "properties": { 69 + "id": { 70 + "type": "string" 71 + }, 72 + "type": { 73 + "type": "string", 74 + "knownValues": ["feed", "list", "timeline"] 75 + }, 76 + "value": { 77 + "type": "string" 78 + }, 79 + "pinned": { 80 + "type": "boolean" 81 + } 82 + } 83 + }, 84 + "statusView": { 85 + "type": "object", 86 + "required": ["status", "record"], 87 + "properties": { 88 + "embed": { 89 + "refs": ["app.bsky.embed.external#view"], 90 + "type": "union", 91 + "description": "An optional embed associated with the status." 92 + }, 93 + "record": { 94 + "type": "unknown" 95 + }, 96 + "status": { 97 + "type": "string", 98 + "description": "The status for the account.", 99 + "knownValues": ["app.bsky.actor.status#live"] 100 + }, 101 + "isActive": { 102 + "type": "boolean", 103 + "description": "True if the status is not expired, false if it is expired. Only present if expiration was set." 104 + }, 105 + "expiresAt": { 106 + "type": "string", 107 + "format": "datetime", 108 + "description": "The date when this status will expire. The application might choose to no longer return the status after expiration." 109 + } 110 + } 111 + }, 112 + "preferences": { 113 + "type": "array", 114 + "items": { 115 + "refs": [ 116 + "#adultContentPref", 117 + "#contentLabelPref", 118 + "#savedFeedsPref", 119 + "#savedFeedsPrefV2", 120 + "#personalDetailsPref", 121 + "#feedViewPref", 122 + "#threadViewPref", 123 + "#interestsPref", 124 + "#mutedWordsPref", 125 + "#hiddenPostsPref", 126 + "#bskyAppStatePref", 127 + "#labelersPref", 128 + "#postInteractionSettingsPref", 129 + "#verificationPrefs" 130 + ], 131 + "type": "union" 132 + } 133 + }, 134 + "profileView": { 135 + "type": "object", 136 + "required": ["did", "handle"], 137 + "properties": { 138 + "did": { 139 + "type": "string", 140 + "format": "did" 141 + }, 142 + "debug": { 143 + "type": "unknown", 144 + "description": "Debug information for internal development" 145 + }, 146 + "avatar": { 147 + "type": "string", 148 + "format": "uri" 149 + }, 150 + "handle": { 151 + "type": "string", 152 + "format": "handle" 153 + }, 154 + "labels": { 155 + "type": "array", 156 + "items": { 157 + "ref": "com.atproto.label.defs#label", 158 + "type": "ref" 159 + } 160 + }, 161 + "status": { 162 + "ref": "#statusView", 163 + "type": "ref" 164 + }, 165 + "viewer": { 166 + "ref": "#viewerState", 167 + "type": "ref" 168 + }, 169 + "pronouns": { 170 + "type": "string" 171 + }, 172 + "createdAt": { 173 + "type": "string", 174 + "format": "datetime" 175 + }, 176 + "indexedAt": { 177 + "type": "string", 178 + "format": "datetime" 179 + }, 180 + "associated": { 181 + "ref": "#profileAssociated", 182 + "type": "ref" 183 + }, 184 + "description": { 185 + "type": "string", 186 + "maxLength": 2560, 187 + "maxGraphemes": 256 188 + }, 189 + "displayName": { 190 + "type": "string", 191 + "maxLength": 640, 192 + "maxGraphemes": 64 193 + }, 194 + "verification": { 195 + "ref": "#verificationState", 196 + "type": "ref" 197 + } 198 + } 199 + }, 200 + "viewerState": { 201 + "type": "object", 202 + "properties": { 203 + "muted": { 204 + "type": "boolean" 205 + }, 206 + "blocking": { 207 + "type": "string", 208 + "format": "at-uri" 209 + }, 210 + "blockedBy": { 211 + "type": "boolean" 212 + }, 213 + "following": { 214 + "type": "string", 215 + "format": "at-uri" 216 + }, 217 + "followedBy": { 218 + "type": "string", 219 + "format": "at-uri" 220 + }, 221 + "mutedByList": { 222 + "ref": "app.bsky.graph.defs#listViewBasic", 223 + "type": "ref" 224 + }, 225 + "blockingByList": { 226 + "ref": "app.bsky.graph.defs#listViewBasic", 227 + "type": "ref" 228 + }, 229 + "knownFollowers": { 230 + "ref": "#knownFollowers", 231 + "type": "ref", 232 + "description": "This property is present only in selected cases, as an optimization." 233 + }, 234 + "activitySubscription": { 235 + "ref": "app.bsky.notification.defs#activitySubscription", 236 + "type": "ref", 237 + "description": "This property is present only in selected cases, as an optimization." 238 + } 239 + }, 240 + "description": "Metadata about the requesting account's relationship with the subject account. Only has meaningful content for authed requests." 241 + }, 242 + "feedViewPref": { 243 + "type": "object", 244 + "required": ["feed"], 245 + "properties": { 246 + "feed": { 247 + "type": "string", 248 + "description": "The URI of the feed, or an identifier which describes the feed." 249 + }, 250 + "hideReplies": { 251 + "type": "boolean", 252 + "description": "Hide replies in the feed." 253 + }, 254 + "hideReposts": { 255 + "type": "boolean", 256 + "description": "Hide reposts in the feed." 257 + }, 258 + "hideQuotePosts": { 259 + "type": "boolean", 260 + "description": "Hide quote posts in the feed." 261 + }, 262 + "hideRepliesByLikeCount": { 263 + "type": "integer", 264 + "description": "Hide replies in the feed if they do not have this number of likes." 265 + }, 266 + "hideRepliesByUnfollowed": { 267 + "type": "boolean", 268 + "default": true, 269 + "description": "Hide replies in the feed if they are not by followed users." 270 + } 271 + } 272 + }, 273 + "labelersPref": { 274 + "type": "object", 275 + "required": ["labelers"], 276 + "properties": { 277 + "labelers": { 278 + "type": "array", 279 + "items": { 280 + "ref": "#labelerPrefItem", 281 + "type": "ref" 282 + } 283 + } 284 + } 285 + }, 286 + "interestsPref": { 287 + "type": "object", 288 + "required": ["tags"], 289 + "properties": { 290 + "tags": { 291 + "type": "array", 292 + "items": { 293 + "type": "string", 294 + "maxLength": 640, 295 + "maxGraphemes": 64 296 + }, 297 + "maxLength": 100, 298 + "description": "A list of tags which describe the account owner's interests gathered during onboarding." 299 + } 300 + } 301 + }, 302 + "knownFollowers": { 303 + "type": "object", 304 + "required": ["count", "followers"], 305 + "properties": { 306 + "count": { 307 + "type": "integer" 308 + }, 309 + "followers": { 310 + "type": "array", 311 + "items": { 312 + "ref": "#profileViewBasic", 313 + "type": "ref" 314 + }, 315 + "maxLength": 5, 316 + "minLength": 0 317 + } 318 + }, 319 + "description": "The subject's followers whom you also follow" 320 + }, 321 + "mutedWordsPref": { 322 + "type": "object", 323 + "required": ["items"], 324 + "properties": { 325 + "items": { 326 + "type": "array", 327 + "items": { 328 + "ref": "app.bsky.actor.defs#mutedWord", 329 + "type": "ref" 330 + }, 331 + "description": "A list of words the account owner has muted." 332 + } 333 + } 334 + }, 335 + "savedFeedsPref": { 336 + "type": "object", 337 + "required": ["pinned", "saved"], 338 + "properties": { 339 + "saved": { 340 + "type": "array", 341 + "items": { 342 + "type": "string", 343 + "format": "at-uri" 344 + } 345 + }, 346 + "pinned": { 347 + "type": "array", 348 + "items": { 349 + "type": "string", 350 + "format": "at-uri" 351 + } 352 + }, 353 + "timelineIndex": { 354 + "type": "integer" 355 + } 356 + } 357 + }, 358 + "threadViewPref": { 359 + "type": "object", 360 + "properties": { 361 + "sort": { 362 + "type": "string", 363 + "description": "Sorting mode for threads.", 364 + "knownValues": ["oldest", "newest", "most-likes", "random", "hotness"] 365 + } 366 + } 367 + }, 368 + "hiddenPostsPref": { 369 + "type": "object", 370 + "required": ["items"], 371 + "properties": { 372 + "items": { 373 + "type": "array", 374 + "items": { 375 + "type": "string", 376 + "format": "at-uri" 377 + }, 378 + "description": "A list of URIs of posts the account owner has hidden." 379 + } 380 + } 381 + }, 382 + "labelerPrefItem": { 383 + "type": "object", 384 + "required": ["did"], 385 + "properties": { 386 + "did": { 387 + "type": "string", 388 + "format": "did" 389 + } 390 + } 391 + }, 392 + "mutedWordTarget": { 393 + "type": "string", 394 + "maxLength": 640, 395 + "knownValues": ["content", "tag"], 396 + "maxGraphemes": 64 397 + }, 398 + "adultContentPref": { 399 + "type": "object", 400 + "required": ["enabled"], 401 + "properties": { 402 + "enabled": { 403 + "type": "boolean", 404 + "default": false 405 + } 406 + } 407 + }, 408 + "bskyAppStatePref": { 409 + "type": "object", 410 + "properties": { 411 + "nuxs": { 412 + "type": "array", 413 + "items": { 414 + "ref": "app.bsky.actor.defs#nux", 415 + "type": "ref" 416 + }, 417 + "maxLength": 100, 418 + "description": "Storage for NUXs the user has encountered." 419 + }, 420 + "queuedNudges": { 421 + "type": "array", 422 + "items": { 423 + "type": "string", 424 + "maxLength": 100 425 + }, 426 + "maxLength": 1000, 427 + "description": "An array of tokens which identify nudges (modals, popups, tours, highlight dots) that should be shown to the user." 428 + }, 429 + "activeProgressGuide": { 430 + "ref": "#bskyAppProgressGuide", 431 + "type": "ref" 432 + } 433 + }, 434 + "description": "A grab bag of state that's specific to the bsky.app program. Third-party apps shouldn't use this." 435 + }, 436 + "contentLabelPref": { 437 + "type": "object", 438 + "required": ["label", "visibility"], 439 + "properties": { 440 + "label": { 441 + "type": "string" 442 + }, 443 + "labelerDid": { 444 + "type": "string", 445 + "format": "did", 446 + "description": "Which labeler does this preference apply to? If undefined, applies globally." 447 + }, 448 + "visibility": { 449 + "type": "string", 450 + "knownValues": ["ignore", "show", "warn", "hide"] 451 + } 452 + } 453 + }, 454 + "profileViewBasic": { 455 + "type": "object", 456 + "required": ["did", "handle"], 457 + "properties": { 458 + "did": { 459 + "type": "string", 460 + "format": "did" 461 + }, 462 + "debug": { 463 + "type": "unknown", 464 + "description": "Debug information for internal development" 465 + }, 466 + "avatar": { 467 + "type": "string", 468 + "format": "uri" 469 + }, 470 + "handle": { 471 + "type": "string", 472 + "format": "handle" 473 + }, 474 + "labels": { 475 + "type": "array", 476 + "items": { 477 + "ref": "com.atproto.label.defs#label", 478 + "type": "ref" 479 + } 480 + }, 481 + "status": { 482 + "ref": "#statusView", 483 + "type": "ref" 484 + }, 485 + "viewer": { 486 + "ref": "#viewerState", 487 + "type": "ref" 488 + }, 489 + "pronouns": { 490 + "type": "string" 491 + }, 492 + "createdAt": { 493 + "type": "string", 494 + "format": "datetime" 495 + }, 496 + "associated": { 497 + "ref": "#profileAssociated", 498 + "type": "ref" 499 + }, 500 + "displayName": { 501 + "type": "string", 502 + "maxLength": 640, 503 + "maxGraphemes": 64 504 + }, 505 + "verification": { 506 + "ref": "#verificationState", 507 + "type": "ref" 508 + } 509 + } 510 + }, 511 + "savedFeedsPrefV2": { 512 + "type": "object", 513 + "required": ["items"], 514 + "properties": { 515 + "items": { 516 + "type": "array", 517 + "items": { 518 + "ref": "app.bsky.actor.defs#savedFeed", 519 + "type": "ref" 520 + } 521 + } 522 + } 523 + }, 524 + "verificationView": { 525 + "type": "object", 526 + "required": ["issuer", "uri", "isValid", "createdAt"], 527 + "properties": { 528 + "uri": { 529 + "type": "string", 530 + "format": "at-uri", 531 + "description": "The AT-URI of the verification record." 532 + }, 533 + "issuer": { 534 + "type": "string", 535 + "format": "did", 536 + "description": "The user who issued this verification." 537 + }, 538 + "isValid": { 539 + "type": "boolean", 540 + "description": "True if the verification passes validation, otherwise false." 541 + }, 542 + "createdAt": { 543 + "type": "string", 544 + "format": "datetime", 545 + "description": "Timestamp when the verification was created." 546 + } 547 + }, 548 + "description": "An individual verification for an associated subject." 549 + }, 550 + "profileAssociated": { 551 + "type": "object", 552 + "properties": { 553 + "chat": { 554 + "ref": "#profileAssociatedChat", 555 + "type": "ref" 556 + }, 557 + "lists": { 558 + "type": "integer" 559 + }, 560 + "labeler": { 561 + "type": "boolean" 562 + }, 563 + "feedgens": { 564 + "type": "integer" 565 + }, 566 + "starterPacks": { 567 + "type": "integer" 568 + }, 569 + "activitySubscription": { 570 + "ref": "#profileAssociatedActivitySubscription", 571 + "type": "ref" 572 + } 573 + } 574 + }, 575 + "verificationPrefs": { 576 + "type": "object", 577 + "required": [], 578 + "properties": { 579 + "hideBadges": { 580 + "type": "boolean", 581 + "default": false, 582 + "description": "Hide the blue check badges for verified accounts and trusted verifiers." 583 + } 584 + }, 585 + "description": "Preferences for how verified accounts appear in the app." 586 + }, 587 + "verificationState": { 588 + "type": "object", 589 + "required": ["verifications", "verifiedStatus", "trustedVerifierStatus"], 590 + "properties": { 591 + "verifications": { 592 + "type": "array", 593 + "items": { 594 + "ref": "#verificationView", 595 + "type": "ref" 596 + }, 597 + "description": "All verifications issued by trusted verifiers on behalf of this user. Verifications by untrusted verifiers are not included." 598 + }, 599 + "verifiedStatus": { 600 + "type": "string", 601 + "description": "The user's status as a verified account.", 602 + "knownValues": ["valid", "invalid", "none"] 603 + }, 604 + "trustedVerifierStatus": { 605 + "type": "string", 606 + "description": "The user's status as a trusted verifier.", 607 + "knownValues": ["valid", "invalid", "none"] 608 + } 609 + }, 610 + "description": "Represents the verification information about the user this object is attached to." 611 + }, 612 + "personalDetailsPref": { 613 + "type": "object", 614 + "properties": { 615 + "birthDate": { 616 + "type": "string", 617 + "format": "datetime", 618 + "description": "The birth date of account owner." 619 + } 620 + } 621 + }, 622 + "profileViewDetailed": { 623 + "type": "object", 624 + "required": ["did", "handle"], 625 + "properties": { 626 + "did": { 627 + "type": "string", 628 + "format": "did" 629 + }, 630 + "debug": { 631 + "type": "unknown", 632 + "description": "Debug information for internal development" 633 + }, 634 + "avatar": { 635 + "type": "string", 636 + "format": "uri" 637 + }, 638 + "banner": { 639 + "type": "string", 640 + "format": "uri" 641 + }, 642 + "handle": { 643 + "type": "string", 644 + "format": "handle" 645 + }, 646 + "labels": { 647 + "type": "array", 648 + "items": { 649 + "ref": "com.atproto.label.defs#label", 650 + "type": "ref" 651 + } 652 + }, 653 + "status": { 654 + "ref": "#statusView", 655 + "type": "ref" 656 + }, 657 + "viewer": { 658 + "ref": "#viewerState", 659 + "type": "ref" 660 + }, 661 + "website": { 662 + "type": "string", 663 + "format": "uri" 664 + }, 665 + "pronouns": { 666 + "type": "string" 667 + }, 668 + "createdAt": { 669 + "type": "string", 670 + "format": "datetime" 671 + }, 672 + "indexedAt": { 673 + "type": "string", 674 + "format": "datetime" 675 + }, 676 + "associated": { 677 + "ref": "#profileAssociated", 678 + "type": "ref" 679 + }, 680 + "pinnedPost": { 681 + "ref": "com.atproto.repo.strongRef", 682 + "type": "ref" 683 + }, 684 + "postsCount": { 685 + "type": "integer" 686 + }, 687 + "description": { 688 + "type": "string", 689 + "maxLength": 2560, 690 + "maxGraphemes": 256 691 + }, 692 + "displayName": { 693 + "type": "string", 694 + "maxLength": 640, 695 + "maxGraphemes": 64 696 + }, 697 + "followsCount": { 698 + "type": "integer" 699 + }, 700 + "verification": { 701 + "ref": "#verificationState", 702 + "type": "ref" 703 + }, 704 + "followersCount": { 705 + "type": "integer" 706 + }, 707 + "joinedViaStarterPack": { 708 + "ref": "app.bsky.graph.defs#starterPackViewBasic", 709 + "type": "ref" 710 + } 711 + } 712 + }, 713 + "bskyAppProgressGuide": { 714 + "type": "object", 715 + "required": ["guide"], 716 + "properties": { 717 + "guide": { 718 + "type": "string", 719 + "maxLength": 100 720 + } 721 + }, 722 + "description": "If set, an active progress guide. Once completed, can be set to undefined. Should have unspecced fields tracking progress." 723 + }, 724 + "profileAssociatedChat": { 725 + "type": "object", 726 + "required": ["allowIncoming"], 727 + "properties": { 728 + "allowIncoming": { 729 + "type": "string", 730 + "knownValues": ["all", "none", "following"] 731 + } 732 + } 733 + }, 734 + "postInteractionSettingsPref": { 735 + "type": "object", 736 + "required": [], 737 + "properties": { 738 + "threadgateAllowRules": { 739 + "type": "array", 740 + "items": { 741 + "refs": [ 742 + "app.bsky.feed.threadgate#mentionRule", 743 + "app.bsky.feed.threadgate#followerRule", 744 + "app.bsky.feed.threadgate#followingRule", 745 + "app.bsky.feed.threadgate#listRule" 746 + ], 747 + "type": "union" 748 + }, 749 + "maxLength": 5, 750 + "description": "Matches threadgate record. List of rules defining who can reply to this users posts. If value is an empty array, no one can reply. If value is undefined, anyone can reply." 751 + }, 752 + "postgateEmbeddingRules": { 753 + "type": "array", 754 + "items": { 755 + "refs": ["app.bsky.feed.postgate#disableRule"], 756 + "type": "union" 757 + }, 758 + "maxLength": 5, 759 + "description": "Matches postgate record. List of rules defining who can embed this users posts. If value is an empty array or is undefined, no particular rules apply and anyone can embed." 760 + } 761 + }, 762 + "description": "Default post interaction settings for the account. These values should be applied as default values when creating new posts. These refs should mirror the threadgate and postgate records exactly." 763 + }, 764 + "profileAssociatedActivitySubscription": { 765 + "type": "object", 766 + "required": ["allowSubscriptions"], 767 + "properties": { 768 + "allowSubscriptions": { 769 + "type": "string", 770 + "knownValues": ["followers", "mutuals", "none"] 771 + } 772 + } 773 + } 774 + }, 775 + "$type": "com.atproto.lexicon.schema", 776 + "lexicon": 1 777 + }
+28 -30
lexicons/app/bsky/actor/getProfile.json
··· 1 1 { 2 - "id": "app.bsky.actor.getProfile", 3 - "defs": { 4 - "main": { 5 - "type": "query", 6 - "output": { 7 - "schema": { 8 - "ref": "app.bsky.actor.defs#profileViewDetailed", 9 - "type": "ref" 10 - }, 11 - "encoding": "application/json" 12 - }, 13 - "parameters": { 14 - "type": "params", 15 - "required": [ 16 - "actor" 17 - ], 18 - "properties": { 19 - "actor": { 20 - "type": "string", 21 - "format": "at-identifier", 22 - "description": "Handle or DID of account to fetch profile of." 23 - } 24 - } 25 - }, 26 - "description": "Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth." 27 - } 28 - }, 29 - "$type": "com.atproto.lexicon.schema", 30 - "lexicon": 1 31 - } 2 + "id": "app.bsky.actor.getProfile", 3 + "defs": { 4 + "main": { 5 + "type": "query", 6 + "output": { 7 + "schema": { 8 + "ref": "app.bsky.actor.defs#profileViewDetailed", 9 + "type": "ref" 10 + }, 11 + "encoding": "application/json" 12 + }, 13 + "parameters": { 14 + "type": "params", 15 + "required": ["actor"], 16 + "properties": { 17 + "actor": { 18 + "type": "string", 19 + "format": "at-identifier", 20 + "description": "Handle or DID of account to fetch profile of." 21 + } 22 + } 23 + }, 24 + "description": "Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth." 25 + } 26 + }, 27 + "$type": "com.atproto.lexicon.schema", 28 + "lexicon": 1 29 + }
+66 -74
lexicons/app/bsky/actor/profile.json
··· 1 1 { 2 - "id": "app.bsky.actor.profile", 3 - "defs": { 4 - "main": { 5 - "key": "literal:self", 6 - "type": "record", 7 - "record": { 8 - "type": "object", 9 - "properties": { 10 - "avatar": { 11 - "type": "blob", 12 - "accept": [ 13 - "image/png", 14 - "image/jpeg" 15 - ], 16 - "maxSize": 1000000, 17 - "description": "Small image to be displayed next to posts from account. AKA, 'profile picture'" 18 - }, 19 - "banner": { 20 - "type": "blob", 21 - "accept": [ 22 - "image/png", 23 - "image/jpeg" 24 - ], 25 - "maxSize": 1000000, 26 - "description": "Larger horizontal image to display behind profile view." 27 - }, 28 - "labels": { 29 - "refs": [ 30 - "com.atproto.label.defs#selfLabels" 31 - ], 32 - "type": "union", 33 - "description": "Self-label values, specific to the Bluesky application, on the overall account." 34 - }, 35 - "website": { 36 - "type": "string", 37 - "format": "uri" 38 - }, 39 - "pronouns": { 40 - "type": "string", 41 - "maxLength": 200, 42 - "description": "Free-form pronouns text.", 43 - "maxGraphemes": 20 44 - }, 45 - "createdAt": { 46 - "type": "string", 47 - "format": "datetime" 48 - }, 49 - "pinnedPost": { 50 - "ref": "com.atproto.repo.strongRef", 51 - "type": "ref" 52 - }, 53 - "description": { 54 - "type": "string", 55 - "maxLength": 2560, 56 - "description": "Free-form profile description text.", 57 - "maxGraphemes": 256 58 - }, 59 - "displayName": { 60 - "type": "string", 61 - "maxLength": 640, 62 - "maxGraphemes": 64 63 - }, 64 - "joinedViaStarterPack": { 65 - "ref": "com.atproto.repo.strongRef", 66 - "type": "ref" 67 - } 68 - } 69 - }, 70 - "description": "A declaration of a Bluesky account profile." 71 - } 72 - }, 73 - "$type": "com.atproto.lexicon.schema", 74 - "lexicon": 1 75 - } 2 + "id": "app.bsky.actor.profile", 3 + "defs": { 4 + "main": { 5 + "key": "literal:self", 6 + "type": "record", 7 + "record": { 8 + "type": "object", 9 + "properties": { 10 + "avatar": { 11 + "type": "blob", 12 + "accept": ["image/png", "image/jpeg"], 13 + "maxSize": 1000000, 14 + "description": "Small image to be displayed next to posts from account. AKA, 'profile picture'" 15 + }, 16 + "banner": { 17 + "type": "blob", 18 + "accept": ["image/png", "image/jpeg"], 19 + "maxSize": 1000000, 20 + "description": "Larger horizontal image to display behind profile view." 21 + }, 22 + "labels": { 23 + "refs": ["com.atproto.label.defs#selfLabels"], 24 + "type": "union", 25 + "description": "Self-label values, specific to the Bluesky application, on the overall account." 26 + }, 27 + "website": { 28 + "type": "string", 29 + "format": "uri" 30 + }, 31 + "pronouns": { 32 + "type": "string", 33 + "maxLength": 200, 34 + "description": "Free-form pronouns text.", 35 + "maxGraphemes": 20 36 + }, 37 + "createdAt": { 38 + "type": "string", 39 + "format": "datetime" 40 + }, 41 + "pinnedPost": { 42 + "ref": "com.atproto.repo.strongRef", 43 + "type": "ref" 44 + }, 45 + "description": { 46 + "type": "string", 47 + "maxLength": 2560, 48 + "description": "Free-form profile description text.", 49 + "maxGraphemes": 256 50 + }, 51 + "displayName": { 52 + "type": "string", 53 + "maxLength": 640, 54 + "maxGraphemes": 64 55 + }, 56 + "joinedViaStarterPack": { 57 + "ref": "com.atproto.repo.strongRef", 58 + "type": "ref" 59 + } 60 + } 61 + }, 62 + "description": "A declaration of a Bluesky account profile." 63 + } 64 + }, 65 + "$type": "com.atproto.lexicon.schema", 66 + "lexicon": 1 67 + }
+40 -47
lexicons/app/bsky/actor/status.json
··· 1 1 { 2 - "id": "app.bsky.actor.status", 3 - "defs": { 4 - "live": { 5 - "type": "token", 6 - "description": "Advertises an account as currently offering live content." 7 - }, 8 - "main": { 9 - "key": "literal:self", 10 - "type": "record", 11 - "record": { 12 - "type": "object", 13 - "required": [ 14 - "status", 15 - "createdAt" 16 - ], 17 - "properties": { 18 - "embed": { 19 - "refs": [ 20 - "app.bsky.embed.external" 21 - ], 22 - "type": "union", 23 - "description": "An optional embed associated with the status." 24 - }, 25 - "status": { 26 - "type": "string", 27 - "description": "The status for the account.", 28 - "knownValues": [ 29 - "app.bsky.actor.status#live" 30 - ] 31 - }, 32 - "createdAt": { 33 - "type": "string", 34 - "format": "datetime" 35 - }, 36 - "durationMinutes": { 37 - "type": "integer", 38 - "minimum": 1, 39 - "description": "The duration of the status in minutes. Applications can choose to impose minimum and maximum limits." 40 - } 41 - } 42 - }, 43 - "description": "A declaration of a Bluesky account status." 44 - } 45 - }, 46 - "$type": "com.atproto.lexicon.schema", 47 - "lexicon": 1 48 - } 2 + "id": "app.bsky.actor.status", 3 + "defs": { 4 + "live": { 5 + "type": "token", 6 + "description": "Advertises an account as currently offering live content." 7 + }, 8 + "main": { 9 + "key": "literal:self", 10 + "type": "record", 11 + "record": { 12 + "type": "object", 13 + "required": ["status", "createdAt"], 14 + "properties": { 15 + "embed": { 16 + "refs": ["app.bsky.embed.external"], 17 + "type": "union", 18 + "description": "An optional embed associated with the status." 19 + }, 20 + "status": { 21 + "type": "string", 22 + "description": "The status for the account.", 23 + "knownValues": ["app.bsky.actor.status#live"] 24 + }, 25 + "createdAt": { 26 + "type": "string", 27 + "format": "datetime" 28 + }, 29 + "durationMinutes": { 30 + "type": "integer", 31 + "minimum": 1, 32 + "description": "The duration of the status in minutes. Applications can choose to impose minimum and maximum limits." 33 + } 34 + } 35 + }, 36 + "description": "A declaration of a Bluesky account status." 37 + } 38 + }, 39 + "$type": "com.atproto.lexicon.schema", 40 + "lexicon": 1 41 + }
+21 -24
lexicons/app/bsky/embed/defs.json
··· 1 1 { 2 - "id": "app.bsky.embed.defs", 3 - "defs": { 4 - "aspectRatio": { 5 - "type": "object", 6 - "required": [ 7 - "width", 8 - "height" 9 - ], 10 - "properties": { 11 - "width": { 12 - "type": "integer", 13 - "minimum": 1 14 - }, 15 - "height": { 16 - "type": "integer", 17 - "minimum": 1 18 - } 19 - }, 20 - "description": "width:height represents an aspect ratio. It may be approximate, and may not correspond to absolute dimensions in any given unit." 21 - } 22 - }, 23 - "$type": "com.atproto.lexicon.schema", 24 - "lexicon": 1 25 - } 2 + "id": "app.bsky.embed.defs", 3 + "defs": { 4 + "aspectRatio": { 5 + "type": "object", 6 + "required": ["width", "height"], 7 + "properties": { 8 + "width": { 9 + "type": "integer", 10 + "minimum": 1 11 + }, 12 + "height": { 13 + "type": "integer", 14 + "minimum": 1 15 + } 16 + }, 17 + "description": "width:height represents an aspect ratio. It may be approximate, and may not correspond to absolute dimensions in any given unit." 18 + } 19 + }, 20 + "$type": "com.atproto.lexicon.schema", 21 + "lexicon": 1 22 + }
+68 -82
lexicons/app/bsky/embed/external.json
··· 1 1 { 2 - "id": "app.bsky.embed.external", 3 - "defs": { 4 - "main": { 5 - "type": "object", 6 - "required": [ 7 - "external" 8 - ], 9 - "properties": { 10 - "external": { 11 - "ref": "#external", 12 - "type": "ref" 13 - } 14 - }, 15 - "description": "A representation of some externally linked content (eg, a URL and 'card'), embedded in a Bluesky record (eg, a post)." 16 - }, 17 - "view": { 18 - "type": "object", 19 - "required": [ 20 - "external" 21 - ], 22 - "properties": { 23 - "external": { 24 - "ref": "#viewExternal", 25 - "type": "ref" 26 - } 27 - } 28 - }, 29 - "external": { 30 - "type": "object", 31 - "required": [ 32 - "uri", 33 - "title", 34 - "description" 35 - ], 36 - "properties": { 37 - "uri": { 38 - "type": "string", 39 - "format": "uri" 40 - }, 41 - "thumb": { 42 - "type": "blob", 43 - "accept": [ 44 - "image/*" 45 - ], 46 - "maxSize": 1000000 47 - }, 48 - "title": { 49 - "type": "string" 50 - }, 51 - "description": { 52 - "type": "string" 53 - } 54 - } 55 - }, 56 - "viewExternal": { 57 - "type": "object", 58 - "required": [ 59 - "uri", 60 - "title", 61 - "description" 62 - ], 63 - "properties": { 64 - "uri": { 65 - "type": "string", 66 - "format": "uri" 67 - }, 68 - "thumb": { 69 - "type": "string", 70 - "format": "uri" 71 - }, 72 - "title": { 73 - "type": "string" 74 - }, 75 - "description": { 76 - "type": "string" 77 - } 78 - } 79 - } 80 - }, 81 - "$type": "com.atproto.lexicon.schema", 82 - "lexicon": 1 83 - } 2 + "id": "app.bsky.embed.external", 3 + "defs": { 4 + "main": { 5 + "type": "object", 6 + "required": ["external"], 7 + "properties": { 8 + "external": { 9 + "ref": "#external", 10 + "type": "ref" 11 + } 12 + }, 13 + "description": "A representation of some externally linked content (eg, a URL and 'card'), embedded in a Bluesky record (eg, a post)." 14 + }, 15 + "view": { 16 + "type": "object", 17 + "required": ["external"], 18 + "properties": { 19 + "external": { 20 + "ref": "#viewExternal", 21 + "type": "ref" 22 + } 23 + } 24 + }, 25 + "external": { 26 + "type": "object", 27 + "required": ["uri", "title", "description"], 28 + "properties": { 29 + "uri": { 30 + "type": "string", 31 + "format": "uri" 32 + }, 33 + "thumb": { 34 + "type": "blob", 35 + "accept": ["image/*"], 36 + "maxSize": 1000000 37 + }, 38 + "title": { 39 + "type": "string" 40 + }, 41 + "description": { 42 + "type": "string" 43 + } 44 + } 45 + }, 46 + "viewExternal": { 47 + "type": "object", 48 + "required": ["uri", "title", "description"], 49 + "properties": { 50 + "uri": { 51 + "type": "string", 52 + "format": "uri" 53 + }, 54 + "thumb": { 55 + "type": "string", 56 + "format": "uri" 57 + }, 58 + "title": { 59 + "type": "string" 60 + }, 61 + "description": { 62 + "type": "string" 63 + } 64 + } 65 + } 66 + }, 67 + "$type": "com.atproto.lexicon.schema", 68 + "lexicon": 1 69 + }
+78 -91
lexicons/app/bsky/embed/images.json
··· 1 1 { 2 - "id": "app.bsky.embed.images", 3 - "defs": { 4 - "main": { 5 - "type": "object", 6 - "required": [ 7 - "images" 8 - ], 9 - "properties": { 10 - "images": { 11 - "type": "array", 12 - "items": { 13 - "ref": "#image", 14 - "type": "ref" 15 - }, 16 - "maxLength": 4 17 - } 18 - } 19 - }, 20 - "view": { 21 - "type": "object", 22 - "required": [ 23 - "images" 24 - ], 25 - "properties": { 26 - "images": { 27 - "type": "array", 28 - "items": { 29 - "ref": "#viewImage", 30 - "type": "ref" 31 - }, 32 - "maxLength": 4 33 - } 34 - } 35 - }, 36 - "image": { 37 - "type": "object", 38 - "required": [ 39 - "image", 40 - "alt" 41 - ], 42 - "properties": { 43 - "alt": { 44 - "type": "string", 45 - "description": "Alt text description of the image, for accessibility." 46 - }, 47 - "image": { 48 - "type": "blob", 49 - "accept": [ 50 - "image/*" 51 - ], 52 - "maxSize": 1000000 53 - }, 54 - "aspectRatio": { 55 - "ref": "app.bsky.embed.defs#aspectRatio", 56 - "type": "ref" 57 - } 58 - } 59 - }, 60 - "viewImage": { 61 - "type": "object", 62 - "required": [ 63 - "thumb", 64 - "fullsize", 65 - "alt" 66 - ], 67 - "properties": { 68 - "alt": { 69 - "type": "string", 70 - "description": "Alt text description of the image, for accessibility." 71 - }, 72 - "thumb": { 73 - "type": "string", 74 - "format": "uri", 75 - "description": "Fully-qualified URL where a thumbnail of the image can be fetched. For example, CDN location provided by the App View." 76 - }, 77 - "fullsize": { 78 - "type": "string", 79 - "format": "uri", 80 - "description": "Fully-qualified URL where a large version of the image can be fetched. May or may not be the exact original blob. For example, CDN location provided by the App View." 81 - }, 82 - "aspectRatio": { 83 - "ref": "app.bsky.embed.defs#aspectRatio", 84 - "type": "ref" 85 - } 86 - } 87 - } 88 - }, 89 - "$type": "com.atproto.lexicon.schema", 90 - "lexicon": 1, 91 - "description": "A set of images embedded in a Bluesky record (eg, a post)." 92 - } 2 + "id": "app.bsky.embed.images", 3 + "defs": { 4 + "main": { 5 + "type": "object", 6 + "required": ["images"], 7 + "properties": { 8 + "images": { 9 + "type": "array", 10 + "items": { 11 + "ref": "#image", 12 + "type": "ref" 13 + }, 14 + "maxLength": 4 15 + } 16 + } 17 + }, 18 + "view": { 19 + "type": "object", 20 + "required": ["images"], 21 + "properties": { 22 + "images": { 23 + "type": "array", 24 + "items": { 25 + "ref": "#viewImage", 26 + "type": "ref" 27 + }, 28 + "maxLength": 4 29 + } 30 + } 31 + }, 32 + "image": { 33 + "type": "object", 34 + "required": ["image", "alt"], 35 + "properties": { 36 + "alt": { 37 + "type": "string", 38 + "description": "Alt text description of the image, for accessibility." 39 + }, 40 + "image": { 41 + "type": "blob", 42 + "accept": ["image/*"], 43 + "maxSize": 1000000 44 + }, 45 + "aspectRatio": { 46 + "ref": "app.bsky.embed.defs#aspectRatio", 47 + "type": "ref" 48 + } 49 + } 50 + }, 51 + "viewImage": { 52 + "type": "object", 53 + "required": ["thumb", "fullsize", "alt"], 54 + "properties": { 55 + "alt": { 56 + "type": "string", 57 + "description": "Alt text description of the image, for accessibility." 58 + }, 59 + "thumb": { 60 + "type": "string", 61 + "format": "uri", 62 + "description": "Fully-qualified URL where a thumbnail of the image can be fetched. For example, CDN location provided by the App View." 63 + }, 64 + "fullsize": { 65 + "type": "string", 66 + "format": "uri", 67 + "description": "Fully-qualified URL where a large version of the image can be fetched. May or may not be the exact original blob. For example, CDN location provided by the App View." 68 + }, 69 + "aspectRatio": { 70 + "ref": "app.bsky.embed.defs#aspectRatio", 71 + "type": "ref" 72 + } 73 + } 74 + } 75 + }, 76 + "$type": "com.atproto.lexicon.schema", 77 + "lexicon": 1, 78 + "description": "A set of images embedded in a Bluesky record (eg, a post)." 79 + }
+140 -160
lexicons/app/bsky/embed/record.json
··· 1 1 { 2 - "id": "app.bsky.embed.record", 3 - "defs": { 4 - "main": { 5 - "type": "object", 6 - "required": [ 7 - "record" 8 - ], 9 - "properties": { 10 - "record": { 11 - "ref": "com.atproto.repo.strongRef", 12 - "type": "ref" 13 - } 14 - } 15 - }, 16 - "view": { 17 - "type": "object", 18 - "required": [ 19 - "record" 20 - ], 21 - "properties": { 22 - "record": { 23 - "refs": [ 24 - "#viewRecord", 25 - "#viewNotFound", 26 - "#viewBlocked", 27 - "#viewDetached", 28 - "app.bsky.feed.defs#generatorView", 29 - "app.bsky.graph.defs#listView", 30 - "app.bsky.labeler.defs#labelerView", 31 - "app.bsky.graph.defs#starterPackViewBasic" 32 - ], 33 - "type": "union" 34 - } 35 - } 36 - }, 37 - "viewRecord": { 38 - "type": "object", 39 - "required": [ 40 - "uri", 41 - "cid", 42 - "author", 43 - "value", 44 - "indexedAt" 45 - ], 46 - "properties": { 47 - "cid": { 48 - "type": "string", 49 - "format": "cid" 50 - }, 51 - "uri": { 52 - "type": "string", 53 - "format": "at-uri" 54 - }, 55 - "value": { 56 - "type": "unknown", 57 - "description": "The record data itself." 58 - }, 59 - "author": { 60 - "ref": "app.bsky.actor.defs#profileViewBasic", 61 - "type": "ref" 62 - }, 63 - "embeds": { 64 - "type": "array", 65 - "items": { 66 - "refs": [ 67 - "app.bsky.embed.images#view", 68 - "app.bsky.embed.video#view", 69 - "app.bsky.embed.external#view", 70 - "app.bsky.embed.record#view", 71 - "app.bsky.embed.recordWithMedia#view" 72 - ], 73 - "type": "union" 74 - } 75 - }, 76 - "labels": { 77 - "type": "array", 78 - "items": { 79 - "ref": "com.atproto.label.defs#label", 80 - "type": "ref" 81 - } 82 - }, 83 - "indexedAt": { 84 - "type": "string", 85 - "format": "datetime" 86 - }, 87 - "likeCount": { 88 - "type": "integer" 89 - }, 90 - "quoteCount": { 91 - "type": "integer" 92 - }, 93 - "replyCount": { 94 - "type": "integer" 95 - }, 96 - "repostCount": { 97 - "type": "integer" 98 - } 99 - } 100 - }, 101 - "viewBlocked": { 102 - "type": "object", 103 - "required": [ 104 - "uri", 105 - "blocked", 106 - "author" 107 - ], 108 - "properties": { 109 - "uri": { 110 - "type": "string", 111 - "format": "at-uri" 112 - }, 113 - "author": { 114 - "ref": "app.bsky.feed.defs#blockedAuthor", 115 - "type": "ref" 116 - }, 117 - "blocked": { 118 - "type": "boolean", 119 - "const": true 120 - } 121 - } 122 - }, 123 - "viewDetached": { 124 - "type": "object", 125 - "required": [ 126 - "uri", 127 - "detached" 128 - ], 129 - "properties": { 130 - "uri": { 131 - "type": "string", 132 - "format": "at-uri" 133 - }, 134 - "detached": { 135 - "type": "boolean", 136 - "const": true 137 - } 138 - } 139 - }, 140 - "viewNotFound": { 141 - "type": "object", 142 - "required": [ 143 - "uri", 144 - "notFound" 145 - ], 146 - "properties": { 147 - "uri": { 148 - "type": "string", 149 - "format": "at-uri" 150 - }, 151 - "notFound": { 152 - "type": "boolean", 153 - "const": true 154 - } 155 - } 156 - } 157 - }, 158 - "$type": "com.atproto.lexicon.schema", 159 - "lexicon": 1, 160 - "description": "A representation of a record embedded in a Bluesky record (eg, a post). For example, a quote-post, or sharing a feed generator record." 161 - } 2 + "id": "app.bsky.embed.record", 3 + "defs": { 4 + "main": { 5 + "type": "object", 6 + "required": ["record"], 7 + "properties": { 8 + "record": { 9 + "ref": "com.atproto.repo.strongRef", 10 + "type": "ref" 11 + } 12 + } 13 + }, 14 + "view": { 15 + "type": "object", 16 + "required": ["record"], 17 + "properties": { 18 + "record": { 19 + "refs": [ 20 + "#viewRecord", 21 + "#viewNotFound", 22 + "#viewBlocked", 23 + "#viewDetached", 24 + "app.bsky.feed.defs#generatorView", 25 + "app.bsky.graph.defs#listView", 26 + "app.bsky.labeler.defs#labelerView", 27 + "app.bsky.graph.defs#starterPackViewBasic" 28 + ], 29 + "type": "union" 30 + } 31 + } 32 + }, 33 + "viewRecord": { 34 + "type": "object", 35 + "required": ["uri", "cid", "author", "value", "indexedAt"], 36 + "properties": { 37 + "cid": { 38 + "type": "string", 39 + "format": "cid" 40 + }, 41 + "uri": { 42 + "type": "string", 43 + "format": "at-uri" 44 + }, 45 + "value": { 46 + "type": "unknown", 47 + "description": "The record data itself." 48 + }, 49 + "author": { 50 + "ref": "app.bsky.actor.defs#profileViewBasic", 51 + "type": "ref" 52 + }, 53 + "embeds": { 54 + "type": "array", 55 + "items": { 56 + "refs": [ 57 + "app.bsky.embed.images#view", 58 + "app.bsky.embed.video#view", 59 + "app.bsky.embed.external#view", 60 + "app.bsky.embed.record#view", 61 + "app.bsky.embed.recordWithMedia#view" 62 + ], 63 + "type": "union" 64 + } 65 + }, 66 + "labels": { 67 + "type": "array", 68 + "items": { 69 + "ref": "com.atproto.label.defs#label", 70 + "type": "ref" 71 + } 72 + }, 73 + "indexedAt": { 74 + "type": "string", 75 + "format": "datetime" 76 + }, 77 + "likeCount": { 78 + "type": "integer" 79 + }, 80 + "quoteCount": { 81 + "type": "integer" 82 + }, 83 + "replyCount": { 84 + "type": "integer" 85 + }, 86 + "repostCount": { 87 + "type": "integer" 88 + } 89 + } 90 + }, 91 + "viewBlocked": { 92 + "type": "object", 93 + "required": ["uri", "blocked", "author"], 94 + "properties": { 95 + "uri": { 96 + "type": "string", 97 + "format": "at-uri" 98 + }, 99 + "author": { 100 + "ref": "app.bsky.feed.defs#blockedAuthor", 101 + "type": "ref" 102 + }, 103 + "blocked": { 104 + "type": "boolean", 105 + "const": true 106 + } 107 + } 108 + }, 109 + "viewDetached": { 110 + "type": "object", 111 + "required": ["uri", "detached"], 112 + "properties": { 113 + "uri": { 114 + "type": "string", 115 + "format": "at-uri" 116 + }, 117 + "detached": { 118 + "type": "boolean", 119 + "const": true 120 + } 121 + } 122 + }, 123 + "viewNotFound": { 124 + "type": "object", 125 + "required": ["uri", "notFound"], 126 + "properties": { 127 + "uri": { 128 + "type": "string", 129 + "format": "at-uri" 130 + }, 131 + "notFound": { 132 + "type": "boolean", 133 + "const": true 134 + } 135 + } 136 + } 137 + }, 138 + "$type": "com.atproto.lexicon.schema", 139 + "lexicon": 1, 140 + "description": "A representation of a record embedded in a Bluesky record (eg, a post). For example, a quote-post, or sharing a feed generator record." 141 + }
+39 -49
lexicons/app/bsky/embed/recordWithMedia.json
··· 1 1 { 2 - "id": "app.bsky.embed.recordWithMedia", 3 - "defs": { 4 - "main": { 5 - "type": "object", 6 - "required": [ 7 - "record", 8 - "media" 9 - ], 10 - "properties": { 11 - "media": { 12 - "refs": [ 13 - "app.bsky.embed.images", 14 - "app.bsky.embed.video", 15 - "app.bsky.embed.external" 16 - ], 17 - "type": "union" 18 - }, 19 - "record": { 20 - "ref": "app.bsky.embed.record", 21 - "type": "ref" 22 - } 23 - } 24 - }, 25 - "view": { 26 - "type": "object", 27 - "required": [ 28 - "record", 29 - "media" 30 - ], 31 - "properties": { 32 - "media": { 33 - "refs": [ 34 - "app.bsky.embed.images#view", 35 - "app.bsky.embed.video#view", 36 - "app.bsky.embed.external#view" 37 - ], 38 - "type": "union" 39 - }, 40 - "record": { 41 - "ref": "app.bsky.embed.record#view", 42 - "type": "ref" 43 - } 44 - } 45 - } 46 - }, 47 - "$type": "com.atproto.lexicon.schema", 48 - "lexicon": 1, 49 - "description": "A representation of a record embedded in a Bluesky record (eg, a post), alongside other compatible embeds. For example, a quote post and image, or a quote post and external URL card." 50 - } 2 + "id": "app.bsky.embed.recordWithMedia", 3 + "defs": { 4 + "main": { 5 + "type": "object", 6 + "required": ["record", "media"], 7 + "properties": { 8 + "media": { 9 + "refs": ["app.bsky.embed.images", "app.bsky.embed.video", "app.bsky.embed.external"], 10 + "type": "union" 11 + }, 12 + "record": { 13 + "ref": "app.bsky.embed.record", 14 + "type": "ref" 15 + } 16 + } 17 + }, 18 + "view": { 19 + "type": "object", 20 + "required": ["record", "media"], 21 + "properties": { 22 + "media": { 23 + "refs": [ 24 + "app.bsky.embed.images#view", 25 + "app.bsky.embed.video#view", 26 + "app.bsky.embed.external#view" 27 + ], 28 + "type": "union" 29 + }, 30 + "record": { 31 + "ref": "app.bsky.embed.record#view", 32 + "type": "ref" 33 + } 34 + } 35 + } 36 + }, 37 + "$type": "com.atproto.lexicon.schema", 38 + "lexicon": 1, 39 + "description": "A representation of a record embedded in a Bluesky record (eg, a post), alongside other compatible embeds. For example, a quote post and image, or a quote post and external URL card." 40 + }
+79 -91
lexicons/app/bsky/embed/video.json
··· 1 1 { 2 - "id": "app.bsky.embed.video", 3 - "defs": { 4 - "main": { 5 - "type": "object", 6 - "required": [ 7 - "video" 8 - ], 9 - "properties": { 10 - "alt": { 11 - "type": "string", 12 - "maxLength": 10000, 13 - "description": "Alt text description of the video, for accessibility.", 14 - "maxGraphemes": 1000 15 - }, 16 - "video": { 17 - "type": "blob", 18 - "accept": [ 19 - "video/mp4" 20 - ], 21 - "maxSize": 100000000, 22 - "description": "The mp4 video file. May be up to 100mb, formerly limited to 50mb." 23 - }, 24 - "captions": { 25 - "type": "array", 26 - "items": { 27 - "ref": "#caption", 28 - "type": "ref" 29 - }, 30 - "maxLength": 20 31 - }, 32 - "aspectRatio": { 33 - "ref": "app.bsky.embed.defs#aspectRatio", 34 - "type": "ref" 35 - } 36 - } 37 - }, 38 - "view": { 39 - "type": "object", 40 - "required": [ 41 - "cid", 42 - "playlist" 43 - ], 44 - "properties": { 45 - "alt": { 46 - "type": "string", 47 - "maxLength": 10000, 48 - "maxGraphemes": 1000 49 - }, 50 - "cid": { 51 - "type": "string", 52 - "format": "cid" 53 - }, 54 - "playlist": { 55 - "type": "string", 56 - "format": "uri" 57 - }, 58 - "thumbnail": { 59 - "type": "string", 60 - "format": "uri" 61 - }, 62 - "aspectRatio": { 63 - "ref": "app.bsky.embed.defs#aspectRatio", 64 - "type": "ref" 65 - } 66 - } 67 - }, 68 - "caption": { 69 - "type": "object", 70 - "required": [ 71 - "lang", 72 - "file" 73 - ], 74 - "properties": { 75 - "file": { 76 - "type": "blob", 77 - "accept": [ 78 - "text/vtt" 79 - ], 80 - "maxSize": 20000 81 - }, 82 - "lang": { 83 - "type": "string", 84 - "format": "language" 85 - } 86 - } 87 - } 88 - }, 89 - "$type": "com.atproto.lexicon.schema", 90 - "lexicon": 1, 91 - "description": "A video embedded in a Bluesky record (eg, a post)." 92 - } 2 + "id": "app.bsky.embed.video", 3 + "defs": { 4 + "main": { 5 + "type": "object", 6 + "required": ["video"], 7 + "properties": { 8 + "alt": { 9 + "type": "string", 10 + "maxLength": 10000, 11 + "description": "Alt text description of the video, for accessibility.", 12 + "maxGraphemes": 1000 13 + }, 14 + "video": { 15 + "type": "blob", 16 + "accept": ["video/mp4"], 17 + "maxSize": 100000000, 18 + "description": "The mp4 video file. May be up to 100mb, formerly limited to 50mb." 19 + }, 20 + "captions": { 21 + "type": "array", 22 + "items": { 23 + "ref": "#caption", 24 + "type": "ref" 25 + }, 26 + "maxLength": 20 27 + }, 28 + "aspectRatio": { 29 + "ref": "app.bsky.embed.defs#aspectRatio", 30 + "type": "ref" 31 + } 32 + } 33 + }, 34 + "view": { 35 + "type": "object", 36 + "required": ["cid", "playlist"], 37 + "properties": { 38 + "alt": { 39 + "type": "string", 40 + "maxLength": 10000, 41 + "maxGraphemes": 1000 42 + }, 43 + "cid": { 44 + "type": "string", 45 + "format": "cid" 46 + }, 47 + "playlist": { 48 + "type": "string", 49 + "format": "uri" 50 + }, 51 + "thumbnail": { 52 + "type": "string", 53 + "format": "uri" 54 + }, 55 + "aspectRatio": { 56 + "ref": "app.bsky.embed.defs#aspectRatio", 57 + "type": "ref" 58 + } 59 + } 60 + }, 61 + "caption": { 62 + "type": "object", 63 + "required": ["lang", "file"], 64 + "properties": { 65 + "file": { 66 + "type": "blob", 67 + "accept": ["text/vtt"], 68 + "maxSize": 20000 69 + }, 70 + "lang": { 71 + "type": "string", 72 + "format": "language" 73 + } 74 + } 75 + } 76 + }, 77 + "$type": "com.atproto.lexicon.schema", 78 + "lexicon": 1, 79 + "description": "A video embedded in a Bluesky record (eg, a post)." 80 + }
+485 -543
lexicons/app/bsky/feed/defs.json
··· 1 1 { 2 - "id": "app.bsky.feed.defs", 3 - "defs": { 4 - "postView": { 5 - "type": "object", 6 - "required": [ 7 - "uri", 8 - "cid", 9 - "author", 10 - "record", 11 - "indexedAt" 12 - ], 13 - "properties": { 14 - "cid": { 15 - "type": "string", 16 - "format": "cid" 17 - }, 18 - "uri": { 19 - "type": "string", 20 - "format": "at-uri" 21 - }, 22 - "debug": { 23 - "type": "unknown", 24 - "description": "Debug information for internal development" 25 - }, 26 - "embed": { 27 - "refs": [ 28 - "app.bsky.embed.images#view", 29 - "app.bsky.embed.video#view", 30 - "app.bsky.embed.external#view", 31 - "app.bsky.embed.record#view", 32 - "app.bsky.embed.recordWithMedia#view" 33 - ], 34 - "type": "union" 35 - }, 36 - "author": { 37 - "ref": "app.bsky.actor.defs#profileViewBasic", 38 - "type": "ref" 39 - }, 40 - "labels": { 41 - "type": "array", 42 - "items": { 43 - "ref": "com.atproto.label.defs#label", 44 - "type": "ref" 45 - } 46 - }, 47 - "record": { 48 - "type": "unknown" 49 - }, 50 - "viewer": { 51 - "ref": "#viewerState", 52 - "type": "ref" 53 - }, 54 - "indexedAt": { 55 - "type": "string", 56 - "format": "datetime" 57 - }, 58 - "likeCount": { 59 - "type": "integer" 60 - }, 61 - "quoteCount": { 62 - "type": "integer" 63 - }, 64 - "replyCount": { 65 - "type": "integer" 66 - }, 67 - "threadgate": { 68 - "ref": "#threadgateView", 69 - "type": "ref" 70 - }, 71 - "repostCount": { 72 - "type": "integer" 73 - }, 74 - "bookmarkCount": { 75 - "type": "integer" 76 - } 77 - } 78 - }, 79 - "replyRef": { 80 - "type": "object", 81 - "required": [ 82 - "root", 83 - "parent" 84 - ], 85 - "properties": { 86 - "root": { 87 - "refs": [ 88 - "#postView", 89 - "#notFoundPost", 90 - "#blockedPost" 91 - ], 92 - "type": "union" 93 - }, 94 - "parent": { 95 - "refs": [ 96 - "#postView", 97 - "#notFoundPost", 98 - "#blockedPost" 99 - ], 100 - "type": "union" 101 - }, 102 - "grandparentAuthor": { 103 - "ref": "app.bsky.actor.defs#profileViewBasic", 104 - "type": "ref", 105 - "description": "When parent is a reply to another post, this is the author of that post." 106 - } 107 - } 108 - }, 109 - "reasonPin": { 110 - "type": "object", 111 - "properties": {} 112 - }, 113 - "blockedPost": { 114 - "type": "object", 115 - "required": [ 116 - "uri", 117 - "blocked", 118 - "author" 119 - ], 120 - "properties": { 121 - "uri": { 122 - "type": "string", 123 - "format": "at-uri" 124 - }, 125 - "author": { 126 - "ref": "#blockedAuthor", 127 - "type": "ref" 128 - }, 129 - "blocked": { 130 - "type": "boolean", 131 - "const": true 132 - } 133 - } 134 - }, 135 - "interaction": { 136 - "type": "object", 137 - "properties": { 138 - "item": { 139 - "type": "string", 140 - "format": "at-uri" 141 - }, 142 - "event": { 143 - "type": "string", 144 - "knownValues": [ 145 - "app.bsky.feed.defs#requestLess", 146 - "app.bsky.feed.defs#requestMore", 147 - "app.bsky.feed.defs#clickthroughItem", 148 - "app.bsky.feed.defs#clickthroughAuthor", 149 - "app.bsky.feed.defs#clickthroughReposter", 150 - "app.bsky.feed.defs#clickthroughEmbed", 151 - "app.bsky.feed.defs#interactionSeen", 152 - "app.bsky.feed.defs#interactionLike", 153 - "app.bsky.feed.defs#interactionRepost", 154 - "app.bsky.feed.defs#interactionReply", 155 - "app.bsky.feed.defs#interactionQuote", 156 - "app.bsky.feed.defs#interactionShare" 157 - ] 158 - }, 159 - "reqId": { 160 - "type": "string", 161 - "maxLength": 100, 162 - "description": "Unique identifier per request that may be passed back alongside interactions." 163 - }, 164 - "feedContext": { 165 - "type": "string", 166 - "maxLength": 2000, 167 - "description": "Context on a feed item that was originally supplied by the feed generator on getFeedSkeleton." 168 - } 169 - } 170 - }, 171 - "requestLess": { 172 - "type": "token", 173 - "description": "Request that less content like the given feed item be shown in the feed" 174 - }, 175 - "requestMore": { 176 - "type": "token", 177 - "description": "Request that more content like the given feed item be shown in the feed" 178 - }, 179 - "viewerState": { 180 - "type": "object", 181 - "properties": { 182 - "like": { 183 - "type": "string", 184 - "format": "at-uri" 185 - }, 186 - "pinned": { 187 - "type": "boolean" 188 - }, 189 - "repost": { 190 - "type": "string", 191 - "format": "at-uri" 192 - }, 193 - "bookmarked": { 194 - "type": "boolean" 195 - }, 196 - "threadMuted": { 197 - "type": "boolean" 198 - }, 199 - "replyDisabled": { 200 - "type": "boolean" 201 - }, 202 - "embeddingDisabled": { 203 - "type": "boolean" 204 - } 205 - }, 206 - "description": "Metadata about the requesting account's relationship with the subject content. Only has meaningful content for authed requests." 207 - }, 208 - "feedViewPost": { 209 - "type": "object", 210 - "required": [ 211 - "post" 212 - ], 213 - "properties": { 214 - "post": { 215 - "ref": "#postView", 216 - "type": "ref" 217 - }, 218 - "reply": { 219 - "ref": "#replyRef", 220 - "type": "ref" 221 - }, 222 - "reqId": { 223 - "type": "string", 224 - "maxLength": 100, 225 - "description": "Unique identifier per request that may be passed back alongside interactions." 226 - }, 227 - "reason": { 228 - "refs": [ 229 - "#reasonRepost", 230 - "#reasonPin" 231 - ], 232 - "type": "union" 233 - }, 234 - "feedContext": { 235 - "type": "string", 236 - "maxLength": 2000, 237 - "description": "Context provided by feed generator that may be passed back alongside interactions." 238 - } 239 - } 240 - }, 241 - "notFoundPost": { 242 - "type": "object", 243 - "required": [ 244 - "uri", 245 - "notFound" 246 - ], 247 - "properties": { 248 - "uri": { 249 - "type": "string", 250 - "format": "at-uri" 251 - }, 252 - "notFound": { 253 - "type": "boolean", 254 - "const": true 255 - } 256 - } 257 - }, 258 - "reasonRepost": { 259 - "type": "object", 260 - "required": [ 261 - "by", 262 - "indexedAt" 263 - ], 264 - "properties": { 265 - "by": { 266 - "ref": "app.bsky.actor.defs#profileViewBasic", 267 - "type": "ref" 268 - }, 269 - "cid": { 270 - "type": "string", 271 - "format": "cid" 272 - }, 273 - "uri": { 274 - "type": "string", 275 - "format": "at-uri" 276 - }, 277 - "indexedAt": { 278 - "type": "string", 279 - "format": "datetime" 280 - } 281 - } 282 - }, 283 - "blockedAuthor": { 284 - "type": "object", 285 - "required": [ 286 - "did" 287 - ], 288 - "properties": { 289 - "did": { 290 - "type": "string", 291 - "format": "did" 292 - }, 293 - "viewer": { 294 - "ref": "app.bsky.actor.defs#viewerState", 295 - "type": "ref" 296 - } 297 - } 298 - }, 299 - "generatorView": { 300 - "type": "object", 301 - "required": [ 302 - "uri", 303 - "cid", 304 - "did", 305 - "creator", 306 - "displayName", 307 - "indexedAt" 308 - ], 309 - "properties": { 310 - "cid": { 311 - "type": "string", 312 - "format": "cid" 313 - }, 314 - "did": { 315 - "type": "string", 316 - "format": "did" 317 - }, 318 - "uri": { 319 - "type": "string", 320 - "format": "at-uri" 321 - }, 322 - "avatar": { 323 - "type": "string", 324 - "format": "uri" 325 - }, 326 - "labels": { 327 - "type": "array", 328 - "items": { 329 - "ref": "com.atproto.label.defs#label", 330 - "type": "ref" 331 - } 332 - }, 333 - "viewer": { 334 - "ref": "#generatorViewerState", 335 - "type": "ref" 336 - }, 337 - "creator": { 338 - "ref": "app.bsky.actor.defs#profileView", 339 - "type": "ref" 340 - }, 341 - "indexedAt": { 342 - "type": "string", 343 - "format": "datetime" 344 - }, 345 - "likeCount": { 346 - "type": "integer", 347 - "minimum": 0 348 - }, 349 - "contentMode": { 350 - "type": "string", 351 - "knownValues": [ 352 - "app.bsky.feed.defs#contentModeUnspecified", 353 - "app.bsky.feed.defs#contentModeVideo" 354 - ] 355 - }, 356 - "description": { 357 - "type": "string", 358 - "maxLength": 3000, 359 - "maxGraphemes": 300 360 - }, 361 - "displayName": { 362 - "type": "string" 363 - }, 364 - "descriptionFacets": { 365 - "type": "array", 366 - "items": { 367 - "ref": "app.bsky.richtext.facet", 368 - "type": "ref" 369 - } 370 - }, 371 - "acceptsInteractions": { 372 - "type": "boolean" 373 - } 374 - } 375 - }, 376 - "threadContext": { 377 - "type": "object", 378 - "properties": { 379 - "rootAuthorLike": { 380 - "type": "string", 381 - "format": "at-uri" 382 - } 383 - }, 384 - "description": "Metadata about this post within the context of the thread it is in." 385 - }, 386 - "threadViewPost": { 387 - "type": "object", 388 - "required": [ 389 - "post" 390 - ], 391 - "properties": { 392 - "post": { 393 - "ref": "#postView", 394 - "type": "ref" 395 - }, 396 - "parent": { 397 - "refs": [ 398 - "#threadViewPost", 399 - "#notFoundPost", 400 - "#blockedPost" 401 - ], 402 - "type": "union" 403 - }, 404 - "replies": { 405 - "type": "array", 406 - "items": { 407 - "refs": [ 408 - "#threadViewPost", 409 - "#notFoundPost", 410 - "#blockedPost" 411 - ], 412 - "type": "union" 413 - } 414 - }, 415 - "threadContext": { 416 - "ref": "#threadContext", 417 - "type": "ref" 418 - } 419 - } 420 - }, 421 - "threadgateView": { 422 - "type": "object", 423 - "properties": { 424 - "cid": { 425 - "type": "string", 426 - "format": "cid" 427 - }, 428 - "uri": { 429 - "type": "string", 430 - "format": "at-uri" 431 - }, 432 - "lists": { 433 - "type": "array", 434 - "items": { 435 - "ref": "app.bsky.graph.defs#listViewBasic", 436 - "type": "ref" 437 - } 438 - }, 439 - "record": { 440 - "type": "unknown" 441 - } 442 - } 443 - }, 444 - "interactionLike": { 445 - "type": "token", 446 - "description": "User liked the feed item" 447 - }, 448 - "interactionSeen": { 449 - "type": "token", 450 - "description": "Feed item was seen by user" 451 - }, 452 - "clickthroughItem": { 453 - "type": "token", 454 - "description": "User clicked through to the feed item" 455 - }, 456 - "contentModeVideo": { 457 - "type": "token", 458 - "description": "Declares the feed generator returns posts containing app.bsky.embed.video embeds." 459 - }, 460 - "interactionQuote": { 461 - "type": "token", 462 - "description": "User quoted the feed item" 463 - }, 464 - "interactionReply": { 465 - "type": "token", 466 - "description": "User replied to the feed item" 467 - }, 468 - "interactionShare": { 469 - "type": "token", 470 - "description": "User shared the feed item" 471 - }, 472 - "skeletonFeedPost": { 473 - "type": "object", 474 - "required": [ 475 - "post" 476 - ], 477 - "properties": { 478 - "post": { 479 - "type": "string", 480 - "format": "at-uri" 481 - }, 482 - "reason": { 483 - "refs": [ 484 - "#skeletonReasonRepost", 485 - "#skeletonReasonPin" 486 - ], 487 - "type": "union" 488 - }, 489 - "feedContext": { 490 - "type": "string", 491 - "maxLength": 2000, 492 - "description": "Context that will be passed through to client and may be passed to feed generator back alongside interactions." 493 - } 494 - } 495 - }, 496 - "clickthroughEmbed": { 497 - "type": "token", 498 - "description": "User clicked through to the embedded content of the feed item" 499 - }, 500 - "interactionRepost": { 501 - "type": "token", 502 - "description": "User reposted the feed item" 503 - }, 504 - "skeletonReasonPin": { 505 - "type": "object", 506 - "properties": {} 507 - }, 508 - "clickthroughAuthor": { 509 - "type": "token", 510 - "description": "User clicked through to the author of the feed item" 511 - }, 512 - "clickthroughReposter": { 513 - "type": "token", 514 - "description": "User clicked through to the reposter of the feed item" 515 - }, 516 - "generatorViewerState": { 517 - "type": "object", 518 - "properties": { 519 - "like": { 520 - "type": "string", 521 - "format": "at-uri" 522 - } 523 - } 524 - }, 525 - "skeletonReasonRepost": { 526 - "type": "object", 527 - "required": [ 528 - "repost" 529 - ], 530 - "properties": { 531 - "repost": { 532 - "type": "string", 533 - "format": "at-uri" 534 - } 535 - } 536 - }, 537 - "contentModeUnspecified": { 538 - "type": "token", 539 - "description": "Declares the feed generator returns any types of posts." 540 - } 541 - }, 542 - "$type": "com.atproto.lexicon.schema", 543 - "lexicon": 1 544 - } 2 + "id": "app.bsky.feed.defs", 3 + "defs": { 4 + "postView": { 5 + "type": "object", 6 + "required": ["uri", "cid", "author", "record", "indexedAt"], 7 + "properties": { 8 + "cid": { 9 + "type": "string", 10 + "format": "cid" 11 + }, 12 + "uri": { 13 + "type": "string", 14 + "format": "at-uri" 15 + }, 16 + "debug": { 17 + "type": "unknown", 18 + "description": "Debug information for internal development" 19 + }, 20 + "embed": { 21 + "refs": [ 22 + "app.bsky.embed.images#view", 23 + "app.bsky.embed.video#view", 24 + "app.bsky.embed.external#view", 25 + "app.bsky.embed.record#view", 26 + "app.bsky.embed.recordWithMedia#view" 27 + ], 28 + "type": "union" 29 + }, 30 + "author": { 31 + "ref": "app.bsky.actor.defs#profileViewBasic", 32 + "type": "ref" 33 + }, 34 + "labels": { 35 + "type": "array", 36 + "items": { 37 + "ref": "com.atproto.label.defs#label", 38 + "type": "ref" 39 + } 40 + }, 41 + "record": { 42 + "type": "unknown" 43 + }, 44 + "viewer": { 45 + "ref": "#viewerState", 46 + "type": "ref" 47 + }, 48 + "indexedAt": { 49 + "type": "string", 50 + "format": "datetime" 51 + }, 52 + "likeCount": { 53 + "type": "integer" 54 + }, 55 + "quoteCount": { 56 + "type": "integer" 57 + }, 58 + "replyCount": { 59 + "type": "integer" 60 + }, 61 + "threadgate": { 62 + "ref": "#threadgateView", 63 + "type": "ref" 64 + }, 65 + "repostCount": { 66 + "type": "integer" 67 + }, 68 + "bookmarkCount": { 69 + "type": "integer" 70 + } 71 + } 72 + }, 73 + "replyRef": { 74 + "type": "object", 75 + "required": ["root", "parent"], 76 + "properties": { 77 + "root": { 78 + "refs": ["#postView", "#notFoundPost", "#blockedPost"], 79 + "type": "union" 80 + }, 81 + "parent": { 82 + "refs": ["#postView", "#notFoundPost", "#blockedPost"], 83 + "type": "union" 84 + }, 85 + "grandparentAuthor": { 86 + "ref": "app.bsky.actor.defs#profileViewBasic", 87 + "type": "ref", 88 + "description": "When parent is a reply to another post, this is the author of that post." 89 + } 90 + } 91 + }, 92 + "reasonPin": { 93 + "type": "object", 94 + "properties": {} 95 + }, 96 + "blockedPost": { 97 + "type": "object", 98 + "required": ["uri", "blocked", "author"], 99 + "properties": { 100 + "uri": { 101 + "type": "string", 102 + "format": "at-uri" 103 + }, 104 + "author": { 105 + "ref": "#blockedAuthor", 106 + "type": "ref" 107 + }, 108 + "blocked": { 109 + "type": "boolean", 110 + "const": true 111 + } 112 + } 113 + }, 114 + "interaction": { 115 + "type": "object", 116 + "properties": { 117 + "item": { 118 + "type": "string", 119 + "format": "at-uri" 120 + }, 121 + "event": { 122 + "type": "string", 123 + "knownValues": [ 124 + "app.bsky.feed.defs#requestLess", 125 + "app.bsky.feed.defs#requestMore", 126 + "app.bsky.feed.defs#clickthroughItem", 127 + "app.bsky.feed.defs#clickthroughAuthor", 128 + "app.bsky.feed.defs#clickthroughReposter", 129 + "app.bsky.feed.defs#clickthroughEmbed", 130 + "app.bsky.feed.defs#interactionSeen", 131 + "app.bsky.feed.defs#interactionLike", 132 + "app.bsky.feed.defs#interactionRepost", 133 + "app.bsky.feed.defs#interactionReply", 134 + "app.bsky.feed.defs#interactionQuote", 135 + "app.bsky.feed.defs#interactionShare" 136 + ] 137 + }, 138 + "reqId": { 139 + "type": "string", 140 + "maxLength": 100, 141 + "description": "Unique identifier per request that may be passed back alongside interactions." 142 + }, 143 + "feedContext": { 144 + "type": "string", 145 + "maxLength": 2000, 146 + "description": "Context on a feed item that was originally supplied by the feed generator on getFeedSkeleton." 147 + } 148 + } 149 + }, 150 + "requestLess": { 151 + "type": "token", 152 + "description": "Request that less content like the given feed item be shown in the feed" 153 + }, 154 + "requestMore": { 155 + "type": "token", 156 + "description": "Request that more content like the given feed item be shown in the feed" 157 + }, 158 + "viewerState": { 159 + "type": "object", 160 + "properties": { 161 + "like": { 162 + "type": "string", 163 + "format": "at-uri" 164 + }, 165 + "pinned": { 166 + "type": "boolean" 167 + }, 168 + "repost": { 169 + "type": "string", 170 + "format": "at-uri" 171 + }, 172 + "bookmarked": { 173 + "type": "boolean" 174 + }, 175 + "threadMuted": { 176 + "type": "boolean" 177 + }, 178 + "replyDisabled": { 179 + "type": "boolean" 180 + }, 181 + "embeddingDisabled": { 182 + "type": "boolean" 183 + } 184 + }, 185 + "description": "Metadata about the requesting account's relationship with the subject content. Only has meaningful content for authed requests." 186 + }, 187 + "feedViewPost": { 188 + "type": "object", 189 + "required": ["post"], 190 + "properties": { 191 + "post": { 192 + "ref": "#postView", 193 + "type": "ref" 194 + }, 195 + "reply": { 196 + "ref": "#replyRef", 197 + "type": "ref" 198 + }, 199 + "reqId": { 200 + "type": "string", 201 + "maxLength": 100, 202 + "description": "Unique identifier per request that may be passed back alongside interactions." 203 + }, 204 + "reason": { 205 + "refs": ["#reasonRepost", "#reasonPin"], 206 + "type": "union" 207 + }, 208 + "feedContext": { 209 + "type": "string", 210 + "maxLength": 2000, 211 + "description": "Context provided by feed generator that may be passed back alongside interactions." 212 + } 213 + } 214 + }, 215 + "notFoundPost": { 216 + "type": "object", 217 + "required": ["uri", "notFound"], 218 + "properties": { 219 + "uri": { 220 + "type": "string", 221 + "format": "at-uri" 222 + }, 223 + "notFound": { 224 + "type": "boolean", 225 + "const": true 226 + } 227 + } 228 + }, 229 + "reasonRepost": { 230 + "type": "object", 231 + "required": ["by", "indexedAt"], 232 + "properties": { 233 + "by": { 234 + "ref": "app.bsky.actor.defs#profileViewBasic", 235 + "type": "ref" 236 + }, 237 + "cid": { 238 + "type": "string", 239 + "format": "cid" 240 + }, 241 + "uri": { 242 + "type": "string", 243 + "format": "at-uri" 244 + }, 245 + "indexedAt": { 246 + "type": "string", 247 + "format": "datetime" 248 + } 249 + } 250 + }, 251 + "blockedAuthor": { 252 + "type": "object", 253 + "required": ["did"], 254 + "properties": { 255 + "did": { 256 + "type": "string", 257 + "format": "did" 258 + }, 259 + "viewer": { 260 + "ref": "app.bsky.actor.defs#viewerState", 261 + "type": "ref" 262 + } 263 + } 264 + }, 265 + "generatorView": { 266 + "type": "object", 267 + "required": ["uri", "cid", "did", "creator", "displayName", "indexedAt"], 268 + "properties": { 269 + "cid": { 270 + "type": "string", 271 + "format": "cid" 272 + }, 273 + "did": { 274 + "type": "string", 275 + "format": "did" 276 + }, 277 + "uri": { 278 + "type": "string", 279 + "format": "at-uri" 280 + }, 281 + "avatar": { 282 + "type": "string", 283 + "format": "uri" 284 + }, 285 + "labels": { 286 + "type": "array", 287 + "items": { 288 + "ref": "com.atproto.label.defs#label", 289 + "type": "ref" 290 + } 291 + }, 292 + "viewer": { 293 + "ref": "#generatorViewerState", 294 + "type": "ref" 295 + }, 296 + "creator": { 297 + "ref": "app.bsky.actor.defs#profileView", 298 + "type": "ref" 299 + }, 300 + "indexedAt": { 301 + "type": "string", 302 + "format": "datetime" 303 + }, 304 + "likeCount": { 305 + "type": "integer", 306 + "minimum": 0 307 + }, 308 + "contentMode": { 309 + "type": "string", 310 + "knownValues": [ 311 + "app.bsky.feed.defs#contentModeUnspecified", 312 + "app.bsky.feed.defs#contentModeVideo" 313 + ] 314 + }, 315 + "description": { 316 + "type": "string", 317 + "maxLength": 3000, 318 + "maxGraphemes": 300 319 + }, 320 + "displayName": { 321 + "type": "string" 322 + }, 323 + "descriptionFacets": { 324 + "type": "array", 325 + "items": { 326 + "ref": "app.bsky.richtext.facet", 327 + "type": "ref" 328 + } 329 + }, 330 + "acceptsInteractions": { 331 + "type": "boolean" 332 + } 333 + } 334 + }, 335 + "threadContext": { 336 + "type": "object", 337 + "properties": { 338 + "rootAuthorLike": { 339 + "type": "string", 340 + "format": "at-uri" 341 + } 342 + }, 343 + "description": "Metadata about this post within the context of the thread it is in." 344 + }, 345 + "threadViewPost": { 346 + "type": "object", 347 + "required": ["post"], 348 + "properties": { 349 + "post": { 350 + "ref": "#postView", 351 + "type": "ref" 352 + }, 353 + "parent": { 354 + "refs": ["#threadViewPost", "#notFoundPost", "#blockedPost"], 355 + "type": "union" 356 + }, 357 + "replies": { 358 + "type": "array", 359 + "items": { 360 + "refs": ["#threadViewPost", "#notFoundPost", "#blockedPost"], 361 + "type": "union" 362 + } 363 + }, 364 + "threadContext": { 365 + "ref": "#threadContext", 366 + "type": "ref" 367 + } 368 + } 369 + }, 370 + "threadgateView": { 371 + "type": "object", 372 + "properties": { 373 + "cid": { 374 + "type": "string", 375 + "format": "cid" 376 + }, 377 + "uri": { 378 + "type": "string", 379 + "format": "at-uri" 380 + }, 381 + "lists": { 382 + "type": "array", 383 + "items": { 384 + "ref": "app.bsky.graph.defs#listViewBasic", 385 + "type": "ref" 386 + } 387 + }, 388 + "record": { 389 + "type": "unknown" 390 + } 391 + } 392 + }, 393 + "interactionLike": { 394 + "type": "token", 395 + "description": "User liked the feed item" 396 + }, 397 + "interactionSeen": { 398 + "type": "token", 399 + "description": "Feed item was seen by user" 400 + }, 401 + "clickthroughItem": { 402 + "type": "token", 403 + "description": "User clicked through to the feed item" 404 + }, 405 + "contentModeVideo": { 406 + "type": "token", 407 + "description": "Declares the feed generator returns posts containing app.bsky.embed.video embeds." 408 + }, 409 + "interactionQuote": { 410 + "type": "token", 411 + "description": "User quoted the feed item" 412 + }, 413 + "interactionReply": { 414 + "type": "token", 415 + "description": "User replied to the feed item" 416 + }, 417 + "interactionShare": { 418 + "type": "token", 419 + "description": "User shared the feed item" 420 + }, 421 + "skeletonFeedPost": { 422 + "type": "object", 423 + "required": ["post"], 424 + "properties": { 425 + "post": { 426 + "type": "string", 427 + "format": "at-uri" 428 + }, 429 + "reason": { 430 + "refs": ["#skeletonReasonRepost", "#skeletonReasonPin"], 431 + "type": "union" 432 + }, 433 + "feedContext": { 434 + "type": "string", 435 + "maxLength": 2000, 436 + "description": "Context that will be passed through to client and may be passed to feed generator back alongside interactions." 437 + } 438 + } 439 + }, 440 + "clickthroughEmbed": { 441 + "type": "token", 442 + "description": "User clicked through to the embedded content of the feed item" 443 + }, 444 + "interactionRepost": { 445 + "type": "token", 446 + "description": "User reposted the feed item" 447 + }, 448 + "skeletonReasonPin": { 449 + "type": "object", 450 + "properties": {} 451 + }, 452 + "clickthroughAuthor": { 453 + "type": "token", 454 + "description": "User clicked through to the author of the feed item" 455 + }, 456 + "clickthroughReposter": { 457 + "type": "token", 458 + "description": "User clicked through to the reposter of the feed item" 459 + }, 460 + "generatorViewerState": { 461 + "type": "object", 462 + "properties": { 463 + "like": { 464 + "type": "string", 465 + "format": "at-uri" 466 + } 467 + } 468 + }, 469 + "skeletonReasonRepost": { 470 + "type": "object", 471 + "required": ["repost"], 472 + "properties": { 473 + "repost": { 474 + "type": "string", 475 + "format": "at-uri" 476 + } 477 + } 478 + }, 479 + "contentModeUnspecified": { 480 + "type": "token", 481 + "description": "Declares the feed generator returns any types of posts." 482 + } 483 + }, 484 + "$type": "com.atproto.lexicon.schema", 485 + "lexicon": 1 486 + }
+49 -54
lexicons/app/bsky/feed/postgate.json
··· 1 1 { 2 - "id": "app.bsky.feed.postgate", 3 - "defs": { 4 - "main": { 5 - "key": "tid", 6 - "type": "record", 7 - "record": { 8 - "type": "object", 9 - "required": [ 10 - "post", 11 - "createdAt" 12 - ], 13 - "properties": { 14 - "post": { 15 - "type": "string", 16 - "format": "at-uri", 17 - "description": "Reference (AT-URI) to the post record." 18 - }, 19 - "createdAt": { 20 - "type": "string", 21 - "format": "datetime" 22 - }, 23 - "embeddingRules": { 24 - "type": "array", 25 - "items": { 26 - "refs": [ 27 - "#disableRule" 28 - ], 29 - "type": "union" 30 - }, 31 - "maxLength": 5, 32 - "description": "List of rules defining who can embed this post. If value is an empty array or is undefined, no particular rules apply and anyone can embed." 33 - }, 34 - "detachedEmbeddingUris": { 35 - "type": "array", 36 - "items": { 37 - "type": "string", 38 - "format": "at-uri" 39 - }, 40 - "maxLength": 50, 41 - "description": "List of AT-URIs embedding this post that the author has detached from." 42 - } 43 - } 44 - }, 45 - "description": "Record defining interaction rules for a post. The record key (rkey) of the postgate record must match the record key of the post, and that record must be in the same repository." 46 - }, 47 - "disableRule": { 48 - "type": "object", 49 - "properties": {}, 50 - "description": "Disables embedding of this post." 51 - } 52 - }, 53 - "$type": "com.atproto.lexicon.schema", 54 - "lexicon": 1 55 - } 2 + "id": "app.bsky.feed.postgate", 3 + "defs": { 4 + "main": { 5 + "key": "tid", 6 + "type": "record", 7 + "record": { 8 + "type": "object", 9 + "required": ["post", "createdAt"], 10 + "properties": { 11 + "post": { 12 + "type": "string", 13 + "format": "at-uri", 14 + "description": "Reference (AT-URI) to the post record." 15 + }, 16 + "createdAt": { 17 + "type": "string", 18 + "format": "datetime" 19 + }, 20 + "embeddingRules": { 21 + "type": "array", 22 + "items": { 23 + "refs": ["#disableRule"], 24 + "type": "union" 25 + }, 26 + "maxLength": 5, 27 + "description": "List of rules defining who can embed this post. If value is an empty array or is undefined, no particular rules apply and anyone can embed." 28 + }, 29 + "detachedEmbeddingUris": { 30 + "type": "array", 31 + "items": { 32 + "type": "string", 33 + "format": "at-uri" 34 + }, 35 + "maxLength": 50, 36 + "description": "List of AT-URIs embedding this post that the author has detached from." 37 + } 38 + } 39 + }, 40 + "description": "Record defining interaction rules for a post. The record key (rkey) of the postgate record must match the record key of the post, and that record must be in the same repository." 41 + }, 42 + "disableRule": { 43 + "type": "object", 44 + "properties": {}, 45 + "description": "Disables embedding of this post." 46 + } 47 + }, 48 + "$type": "com.atproto.lexicon.schema", 49 + "lexicon": 1 50 + }
+70 -80
lexicons/app/bsky/feed/threadgate.json
··· 1 1 { 2 - "id": "app.bsky.feed.threadgate", 3 - "defs": { 4 - "main": { 5 - "key": "tid", 6 - "type": "record", 7 - "record": { 8 - "type": "object", 9 - "required": [ 10 - "post", 11 - "createdAt" 12 - ], 13 - "properties": { 14 - "post": { 15 - "type": "string", 16 - "format": "at-uri", 17 - "description": "Reference (AT-URI) to the post record." 18 - }, 19 - "allow": { 20 - "type": "array", 21 - "items": { 22 - "refs": [ 23 - "#mentionRule", 24 - "#followerRule", 25 - "#followingRule", 26 - "#listRule" 27 - ], 28 - "type": "union" 29 - }, 30 - "maxLength": 5, 31 - "description": "List of rules defining who can reply to this post. If value is an empty array, no one can reply. If value is undefined, anyone can reply." 32 - }, 33 - "createdAt": { 34 - "type": "string", 35 - "format": "datetime" 36 - }, 37 - "hiddenReplies": { 38 - "type": "array", 39 - "items": { 40 - "type": "string", 41 - "format": "at-uri" 42 - }, 43 - "maxLength": 300, 44 - "description": "List of hidden reply URIs." 45 - } 46 - } 47 - }, 48 - "description": "Record defining interaction gating rules for a thread (aka, reply controls). The record key (rkey) of the threadgate record must match the record key of the thread's root post, and that record must be in the same repository." 49 - }, 50 - "listRule": { 51 - "type": "object", 52 - "required": [ 53 - "list" 54 - ], 55 - "properties": { 56 - "list": { 57 - "type": "string", 58 - "format": "at-uri" 59 - } 60 - }, 61 - "description": "Allow replies from actors on a list." 62 - }, 63 - "mentionRule": { 64 - "type": "object", 65 - "properties": {}, 66 - "description": "Allow replies from actors mentioned in your post." 67 - }, 68 - "followerRule": { 69 - "type": "object", 70 - "properties": {}, 71 - "description": "Allow replies from actors who follow you." 72 - }, 73 - "followingRule": { 74 - "type": "object", 75 - "properties": {}, 76 - "description": "Allow replies from actors you follow." 77 - } 78 - }, 79 - "$type": "com.atproto.lexicon.schema", 80 - "lexicon": 1 81 - } 2 + "id": "app.bsky.feed.threadgate", 3 + "defs": { 4 + "main": { 5 + "key": "tid", 6 + "type": "record", 7 + "record": { 8 + "type": "object", 9 + "required": ["post", "createdAt"], 10 + "properties": { 11 + "post": { 12 + "type": "string", 13 + "format": "at-uri", 14 + "description": "Reference (AT-URI) to the post record." 15 + }, 16 + "allow": { 17 + "type": "array", 18 + "items": { 19 + "refs": ["#mentionRule", "#followerRule", "#followingRule", "#listRule"], 20 + "type": "union" 21 + }, 22 + "maxLength": 5, 23 + "description": "List of rules defining who can reply to this post. If value is an empty array, no one can reply. If value is undefined, anyone can reply." 24 + }, 25 + "createdAt": { 26 + "type": "string", 27 + "format": "datetime" 28 + }, 29 + "hiddenReplies": { 30 + "type": "array", 31 + "items": { 32 + "type": "string", 33 + "format": "at-uri" 34 + }, 35 + "maxLength": 300, 36 + "description": "List of hidden reply URIs." 37 + } 38 + } 39 + }, 40 + "description": "Record defining interaction gating rules for a thread (aka, reply controls). The record key (rkey) of the threadgate record must match the record key of the thread's root post, and that record must be in the same repository." 41 + }, 42 + "listRule": { 43 + "type": "object", 44 + "required": ["list"], 45 + "properties": { 46 + "list": { 47 + "type": "string", 48 + "format": "at-uri" 49 + } 50 + }, 51 + "description": "Allow replies from actors on a list." 52 + }, 53 + "mentionRule": { 54 + "type": "object", 55 + "properties": {}, 56 + "description": "Allow replies from actors mentioned in your post." 57 + }, 58 + "followerRule": { 59 + "type": "object", 60 + "properties": {}, 61 + "description": "Allow replies from actors who follow you." 62 + }, 63 + "followingRule": { 64 + "type": "object", 65 + "properties": {}, 66 + "description": "Allow replies from actors you follow." 67 + } 68 + }, 69 + "$type": "com.atproto.lexicon.schema", 70 + "lexicon": 1 71 + }
+300 -332
lexicons/app/bsky/graph/defs.json
··· 1 1 { 2 - "id": "app.bsky.graph.defs", 3 - "defs": { 4 - "modlist": { 5 - "type": "token", 6 - "description": "A list of actors to apply an aggregate moderation action (mute/block) on." 7 - }, 8 - "listView": { 9 - "type": "object", 10 - "required": [ 11 - "uri", 12 - "cid", 13 - "creator", 14 - "name", 15 - "purpose", 16 - "indexedAt" 17 - ], 18 - "properties": { 19 - "cid": { 20 - "type": "string", 21 - "format": "cid" 22 - }, 23 - "uri": { 24 - "type": "string", 25 - "format": "at-uri" 26 - }, 27 - "name": { 28 - "type": "string", 29 - "maxLength": 64, 30 - "minLength": 1 31 - }, 32 - "avatar": { 33 - "type": "string", 34 - "format": "uri" 35 - }, 36 - "labels": { 37 - "type": "array", 38 - "items": { 39 - "ref": "com.atproto.label.defs#label", 40 - "type": "ref" 41 - } 42 - }, 43 - "viewer": { 44 - "ref": "#listViewerState", 45 - "type": "ref" 46 - }, 47 - "creator": { 48 - "ref": "app.bsky.actor.defs#profileView", 49 - "type": "ref" 50 - }, 51 - "purpose": { 52 - "ref": "#listPurpose", 53 - "type": "ref" 54 - }, 55 - "indexedAt": { 56 - "type": "string", 57 - "format": "datetime" 58 - }, 59 - "description": { 60 - "type": "string", 61 - "maxLength": 3000, 62 - "maxGraphemes": 300 63 - }, 64 - "listItemCount": { 65 - "type": "integer", 66 - "minimum": 0 67 - }, 68 - "descriptionFacets": { 69 - "type": "array", 70 - "items": { 71 - "ref": "app.bsky.richtext.facet", 72 - "type": "ref" 73 - } 74 - } 75 - } 76 - }, 77 - "curatelist": { 78 - "type": "token", 79 - "description": "A list of actors used for curation purposes such as list feeds or interaction gating." 80 - }, 81 - "listPurpose": { 82 - "type": "string", 83 - "knownValues": [ 84 - "app.bsky.graph.defs#modlist", 85 - "app.bsky.graph.defs#curatelist", 86 - "app.bsky.graph.defs#referencelist" 87 - ] 88 - }, 89 - "listItemView": { 90 - "type": "object", 91 - "required": [ 92 - "uri", 93 - "subject" 94 - ], 95 - "properties": { 96 - "uri": { 97 - "type": "string", 98 - "format": "at-uri" 99 - }, 100 - "subject": { 101 - "ref": "app.bsky.actor.defs#profileView", 102 - "type": "ref" 103 - } 104 - } 105 - }, 106 - "relationship": { 107 - "type": "object", 108 - "required": [ 109 - "did" 110 - ], 111 - "properties": { 112 - "did": { 113 - "type": "string", 114 - "format": "did" 115 - }, 116 - "following": { 117 - "type": "string", 118 - "format": "at-uri", 119 - "description": "if the actor follows this DID, this is the AT-URI of the follow record" 120 - }, 121 - "followedBy": { 122 - "type": "string", 123 - "format": "at-uri", 124 - "description": "if the actor is followed by this DID, contains the AT-URI of the follow record" 125 - } 126 - }, 127 - "description": "lists the bi-directional graph relationships between one actor (not indicated in the object), and the target actors (the DID included in the object)" 128 - }, 129 - "listViewBasic": { 130 - "type": "object", 131 - "required": [ 132 - "uri", 133 - "cid", 134 - "name", 135 - "purpose" 136 - ], 137 - "properties": { 138 - "cid": { 139 - "type": "string", 140 - "format": "cid" 141 - }, 142 - "uri": { 143 - "type": "string", 144 - "format": "at-uri" 145 - }, 146 - "name": { 147 - "type": "string", 148 - "maxLength": 64, 149 - "minLength": 1 150 - }, 151 - "avatar": { 152 - "type": "string", 153 - "format": "uri" 154 - }, 155 - "labels": { 156 - "type": "array", 157 - "items": { 158 - "ref": "com.atproto.label.defs#label", 159 - "type": "ref" 160 - } 161 - }, 162 - "viewer": { 163 - "ref": "#listViewerState", 164 - "type": "ref" 165 - }, 166 - "purpose": { 167 - "ref": "#listPurpose", 168 - "type": "ref" 169 - }, 170 - "indexedAt": { 171 - "type": "string", 172 - "format": "datetime" 173 - }, 174 - "listItemCount": { 175 - "type": "integer", 176 - "minimum": 0 177 - } 178 - } 179 - }, 180 - "notFoundActor": { 181 - "type": "object", 182 - "required": [ 183 - "actor", 184 - "notFound" 185 - ], 186 - "properties": { 187 - "actor": { 188 - "type": "string", 189 - "format": "at-identifier" 190 - }, 191 - "notFound": { 192 - "type": "boolean", 193 - "const": true 194 - } 195 - }, 196 - "description": "indicates that a handle or DID could not be resolved" 197 - }, 198 - "referencelist": { 199 - "type": "token", 200 - "description": "A list of actors used for only for reference purposes such as within a starter pack." 201 - }, 202 - "listViewerState": { 203 - "type": "object", 204 - "properties": { 205 - "muted": { 206 - "type": "boolean" 207 - }, 208 - "blocked": { 209 - "type": "string", 210 - "format": "at-uri" 211 - } 212 - } 213 - }, 214 - "starterPackView": { 215 - "type": "object", 216 - "required": [ 217 - "uri", 218 - "cid", 219 - "record", 220 - "creator", 221 - "indexedAt" 222 - ], 223 - "properties": { 224 - "cid": { 225 - "type": "string", 226 - "format": "cid" 227 - }, 228 - "uri": { 229 - "type": "string", 230 - "format": "at-uri" 231 - }, 232 - "list": { 233 - "ref": "#listViewBasic", 234 - "type": "ref" 235 - }, 236 - "feeds": { 237 - "type": "array", 238 - "items": { 239 - "ref": "app.bsky.feed.defs#generatorView", 240 - "type": "ref" 241 - }, 242 - "maxLength": 3 243 - }, 244 - "labels": { 245 - "type": "array", 246 - "items": { 247 - "ref": "com.atproto.label.defs#label", 248 - "type": "ref" 249 - } 250 - }, 251 - "record": { 252 - "type": "unknown" 253 - }, 254 - "creator": { 255 - "ref": "app.bsky.actor.defs#profileViewBasic", 256 - "type": "ref" 257 - }, 258 - "indexedAt": { 259 - "type": "string", 260 - "format": "datetime" 261 - }, 262 - "joinedWeekCount": { 263 - "type": "integer", 264 - "minimum": 0 265 - }, 266 - "listItemsSample": { 267 - "type": "array", 268 - "items": { 269 - "ref": "#listItemView", 270 - "type": "ref" 271 - }, 272 - "maxLength": 12 273 - }, 274 - "joinedAllTimeCount": { 275 - "type": "integer", 276 - "minimum": 0 277 - } 278 - } 279 - }, 280 - "starterPackViewBasic": { 281 - "type": "object", 282 - "required": [ 283 - "uri", 284 - "cid", 285 - "record", 286 - "creator", 287 - "indexedAt" 288 - ], 289 - "properties": { 290 - "cid": { 291 - "type": "string", 292 - "format": "cid" 293 - }, 294 - "uri": { 295 - "type": "string", 296 - "format": "at-uri" 297 - }, 298 - "labels": { 299 - "type": "array", 300 - "items": { 301 - "ref": "com.atproto.label.defs#label", 302 - "type": "ref" 303 - } 304 - }, 305 - "record": { 306 - "type": "unknown" 307 - }, 308 - "creator": { 309 - "ref": "app.bsky.actor.defs#profileViewBasic", 310 - "type": "ref" 311 - }, 312 - "indexedAt": { 313 - "type": "string", 314 - "format": "datetime" 315 - }, 316 - "listItemCount": { 317 - "type": "integer", 318 - "minimum": 0 319 - }, 320 - "joinedWeekCount": { 321 - "type": "integer", 322 - "minimum": 0 323 - }, 324 - "joinedAllTimeCount": { 325 - "type": "integer", 326 - "minimum": 0 327 - } 328 - } 329 - } 330 - }, 331 - "$type": "com.atproto.lexicon.schema", 332 - "lexicon": 1 333 - } 2 + "id": "app.bsky.graph.defs", 3 + "defs": { 4 + "modlist": { 5 + "type": "token", 6 + "description": "A list of actors to apply an aggregate moderation action (mute/block) on." 7 + }, 8 + "listView": { 9 + "type": "object", 10 + "required": ["uri", "cid", "creator", "name", "purpose", "indexedAt"], 11 + "properties": { 12 + "cid": { 13 + "type": "string", 14 + "format": "cid" 15 + }, 16 + "uri": { 17 + "type": "string", 18 + "format": "at-uri" 19 + }, 20 + "name": { 21 + "type": "string", 22 + "maxLength": 64, 23 + "minLength": 1 24 + }, 25 + "avatar": { 26 + "type": "string", 27 + "format": "uri" 28 + }, 29 + "labels": { 30 + "type": "array", 31 + "items": { 32 + "ref": "com.atproto.label.defs#label", 33 + "type": "ref" 34 + } 35 + }, 36 + "viewer": { 37 + "ref": "#listViewerState", 38 + "type": "ref" 39 + }, 40 + "creator": { 41 + "ref": "app.bsky.actor.defs#profileView", 42 + "type": "ref" 43 + }, 44 + "purpose": { 45 + "ref": "#listPurpose", 46 + "type": "ref" 47 + }, 48 + "indexedAt": { 49 + "type": "string", 50 + "format": "datetime" 51 + }, 52 + "description": { 53 + "type": "string", 54 + "maxLength": 3000, 55 + "maxGraphemes": 300 56 + }, 57 + "listItemCount": { 58 + "type": "integer", 59 + "minimum": 0 60 + }, 61 + "descriptionFacets": { 62 + "type": "array", 63 + "items": { 64 + "ref": "app.bsky.richtext.facet", 65 + "type": "ref" 66 + } 67 + } 68 + } 69 + }, 70 + "curatelist": { 71 + "type": "token", 72 + "description": "A list of actors used for curation purposes such as list feeds or interaction gating." 73 + }, 74 + "listPurpose": { 75 + "type": "string", 76 + "knownValues": [ 77 + "app.bsky.graph.defs#modlist", 78 + "app.bsky.graph.defs#curatelist", 79 + "app.bsky.graph.defs#referencelist" 80 + ] 81 + }, 82 + "listItemView": { 83 + "type": "object", 84 + "required": ["uri", "subject"], 85 + "properties": { 86 + "uri": { 87 + "type": "string", 88 + "format": "at-uri" 89 + }, 90 + "subject": { 91 + "ref": "app.bsky.actor.defs#profileView", 92 + "type": "ref" 93 + } 94 + } 95 + }, 96 + "relationship": { 97 + "type": "object", 98 + "required": ["did"], 99 + "properties": { 100 + "did": { 101 + "type": "string", 102 + "format": "did" 103 + }, 104 + "following": { 105 + "type": "string", 106 + "format": "at-uri", 107 + "description": "if the actor follows this DID, this is the AT-URI of the follow record" 108 + }, 109 + "followedBy": { 110 + "type": "string", 111 + "format": "at-uri", 112 + "description": "if the actor is followed by this DID, contains the AT-URI of the follow record" 113 + } 114 + }, 115 + "description": "lists the bi-directional graph relationships between one actor (not indicated in the object), and the target actors (the DID included in the object)" 116 + }, 117 + "listViewBasic": { 118 + "type": "object", 119 + "required": ["uri", "cid", "name", "purpose"], 120 + "properties": { 121 + "cid": { 122 + "type": "string", 123 + "format": "cid" 124 + }, 125 + "uri": { 126 + "type": "string", 127 + "format": "at-uri" 128 + }, 129 + "name": { 130 + "type": "string", 131 + "maxLength": 64, 132 + "minLength": 1 133 + }, 134 + "avatar": { 135 + "type": "string", 136 + "format": "uri" 137 + }, 138 + "labels": { 139 + "type": "array", 140 + "items": { 141 + "ref": "com.atproto.label.defs#label", 142 + "type": "ref" 143 + } 144 + }, 145 + "viewer": { 146 + "ref": "#listViewerState", 147 + "type": "ref" 148 + }, 149 + "purpose": { 150 + "ref": "#listPurpose", 151 + "type": "ref" 152 + }, 153 + "indexedAt": { 154 + "type": "string", 155 + "format": "datetime" 156 + }, 157 + "listItemCount": { 158 + "type": "integer", 159 + "minimum": 0 160 + } 161 + } 162 + }, 163 + "notFoundActor": { 164 + "type": "object", 165 + "required": ["actor", "notFound"], 166 + "properties": { 167 + "actor": { 168 + "type": "string", 169 + "format": "at-identifier" 170 + }, 171 + "notFound": { 172 + "type": "boolean", 173 + "const": true 174 + } 175 + }, 176 + "description": "indicates that a handle or DID could not be resolved" 177 + }, 178 + "referencelist": { 179 + "type": "token", 180 + "description": "A list of actors used for only for reference purposes such as within a starter pack." 181 + }, 182 + "listViewerState": { 183 + "type": "object", 184 + "properties": { 185 + "muted": { 186 + "type": "boolean" 187 + }, 188 + "blocked": { 189 + "type": "string", 190 + "format": "at-uri" 191 + } 192 + } 193 + }, 194 + "starterPackView": { 195 + "type": "object", 196 + "required": ["uri", "cid", "record", "creator", "indexedAt"], 197 + "properties": { 198 + "cid": { 199 + "type": "string", 200 + "format": "cid" 201 + }, 202 + "uri": { 203 + "type": "string", 204 + "format": "at-uri" 205 + }, 206 + "list": { 207 + "ref": "#listViewBasic", 208 + "type": "ref" 209 + }, 210 + "feeds": { 211 + "type": "array", 212 + "items": { 213 + "ref": "app.bsky.feed.defs#generatorView", 214 + "type": "ref" 215 + }, 216 + "maxLength": 3 217 + }, 218 + "labels": { 219 + "type": "array", 220 + "items": { 221 + "ref": "com.atproto.label.defs#label", 222 + "type": "ref" 223 + } 224 + }, 225 + "record": { 226 + "type": "unknown" 227 + }, 228 + "creator": { 229 + "ref": "app.bsky.actor.defs#profileViewBasic", 230 + "type": "ref" 231 + }, 232 + "indexedAt": { 233 + "type": "string", 234 + "format": "datetime" 235 + }, 236 + "joinedWeekCount": { 237 + "type": "integer", 238 + "minimum": 0 239 + }, 240 + "listItemsSample": { 241 + "type": "array", 242 + "items": { 243 + "ref": "#listItemView", 244 + "type": "ref" 245 + }, 246 + "maxLength": 12 247 + }, 248 + "joinedAllTimeCount": { 249 + "type": "integer", 250 + "minimum": 0 251 + } 252 + } 253 + }, 254 + "starterPackViewBasic": { 255 + "type": "object", 256 + "required": ["uri", "cid", "record", "creator", "indexedAt"], 257 + "properties": { 258 + "cid": { 259 + "type": "string", 260 + "format": "cid" 261 + }, 262 + "uri": { 263 + "type": "string", 264 + "format": "at-uri" 265 + }, 266 + "labels": { 267 + "type": "array", 268 + "items": { 269 + "ref": "com.atproto.label.defs#label", 270 + "type": "ref" 271 + } 272 + }, 273 + "record": { 274 + "type": "unknown" 275 + }, 276 + "creator": { 277 + "ref": "app.bsky.actor.defs#profileViewBasic", 278 + "type": "ref" 279 + }, 280 + "indexedAt": { 281 + "type": "string", 282 + "format": "datetime" 283 + }, 284 + "listItemCount": { 285 + "type": "integer", 286 + "minimum": 0 287 + }, 288 + "joinedWeekCount": { 289 + "type": "integer", 290 + "minimum": 0 291 + }, 292 + "joinedAllTimeCount": { 293 + "type": "integer", 294 + "minimum": 0 295 + } 296 + } 297 + } 298 + }, 299 + "$type": "com.atproto.lexicon.schema", 300 + "lexicon": 1 301 + }
+139 -152
lexicons/app/bsky/labeler/defs.json
··· 1 1 { 2 - "id": "app.bsky.labeler.defs", 3 - "defs": { 4 - "labelerView": { 5 - "type": "object", 6 - "required": [ 7 - "uri", 8 - "cid", 9 - "creator", 10 - "indexedAt" 11 - ], 12 - "properties": { 13 - "cid": { 14 - "type": "string", 15 - "format": "cid" 16 - }, 17 - "uri": { 18 - "type": "string", 19 - "format": "at-uri" 20 - }, 21 - "labels": { 22 - "type": "array", 23 - "items": { 24 - "ref": "com.atproto.label.defs#label", 25 - "type": "ref" 26 - } 27 - }, 28 - "viewer": { 29 - "ref": "#labelerViewerState", 30 - "type": "ref" 31 - }, 32 - "creator": { 33 - "ref": "app.bsky.actor.defs#profileView", 34 - "type": "ref" 35 - }, 36 - "indexedAt": { 37 - "type": "string", 38 - "format": "datetime" 39 - }, 40 - "likeCount": { 41 - "type": "integer", 42 - "minimum": 0 43 - } 44 - } 45 - }, 46 - "labelerPolicies": { 47 - "type": "object", 48 - "required": [ 49 - "labelValues" 50 - ], 51 - "properties": { 52 - "labelValues": { 53 - "type": "array", 54 - "items": { 55 - "ref": "com.atproto.label.defs#labelValue", 56 - "type": "ref" 57 - }, 58 - "description": "The label values which this labeler publishes. May include global or custom labels." 59 - }, 60 - "labelValueDefinitions": { 61 - "type": "array", 62 - "items": { 63 - "ref": "com.atproto.label.defs#labelValueDefinition", 64 - "type": "ref" 65 - }, 66 - "description": "Label values created by this labeler and scoped exclusively to it. Labels defined here will override global label definitions for this labeler." 67 - } 68 - } 69 - }, 70 - "labelerViewerState": { 71 - "type": "object", 72 - "properties": { 73 - "like": { 74 - "type": "string", 75 - "format": "at-uri" 76 - } 77 - } 78 - }, 79 - "labelerViewDetailed": { 80 - "type": "object", 81 - "required": [ 82 - "uri", 83 - "cid", 84 - "creator", 85 - "policies", 86 - "indexedAt" 87 - ], 88 - "properties": { 89 - "cid": { 90 - "type": "string", 91 - "format": "cid" 92 - }, 93 - "uri": { 94 - "type": "string", 95 - "format": "at-uri" 96 - }, 97 - "labels": { 98 - "type": "array", 99 - "items": { 100 - "ref": "com.atproto.label.defs#label", 101 - "type": "ref" 102 - } 103 - }, 104 - "viewer": { 105 - "ref": "#labelerViewerState", 106 - "type": "ref" 107 - }, 108 - "creator": { 109 - "ref": "app.bsky.actor.defs#profileView", 110 - "type": "ref" 111 - }, 112 - "policies": { 113 - "ref": "app.bsky.labeler.defs#labelerPolicies", 114 - "type": "ref" 115 - }, 116 - "indexedAt": { 117 - "type": "string", 118 - "format": "datetime" 119 - }, 120 - "likeCount": { 121 - "type": "integer", 122 - "minimum": 0 123 - }, 124 - "reasonTypes": { 125 - "type": "array", 126 - "items": { 127 - "ref": "com.atproto.moderation.defs#reasonType", 128 - "type": "ref" 129 - }, 130 - "description": "The set of report reason 'codes' which are in-scope for this service to review and action. These usually align to policy categories. If not defined (distinct from empty array), all reason types are allowed." 131 - }, 132 - "subjectTypes": { 133 - "type": "array", 134 - "items": { 135 - "ref": "com.atproto.moderation.defs#subjectType", 136 - "type": "ref" 137 - }, 138 - "description": "The set of subject types (account, record, etc) this service accepts reports on." 139 - }, 140 - "subjectCollections": { 141 - "type": "array", 142 - "items": { 143 - "type": "string", 144 - "format": "nsid" 145 - }, 146 - "description": "Set of record types (collection NSIDs) which can be reported to this service. If not defined (distinct from empty array), default is any record type." 147 - } 148 - } 149 - } 150 - }, 151 - "$type": "com.atproto.lexicon.schema", 152 - "lexicon": 1 153 - } 2 + "id": "app.bsky.labeler.defs", 3 + "defs": { 4 + "labelerView": { 5 + "type": "object", 6 + "required": ["uri", "cid", "creator", "indexedAt"], 7 + "properties": { 8 + "cid": { 9 + "type": "string", 10 + "format": "cid" 11 + }, 12 + "uri": { 13 + "type": "string", 14 + "format": "at-uri" 15 + }, 16 + "labels": { 17 + "type": "array", 18 + "items": { 19 + "ref": "com.atproto.label.defs#label", 20 + "type": "ref" 21 + } 22 + }, 23 + "viewer": { 24 + "ref": "#labelerViewerState", 25 + "type": "ref" 26 + }, 27 + "creator": { 28 + "ref": "app.bsky.actor.defs#profileView", 29 + "type": "ref" 30 + }, 31 + "indexedAt": { 32 + "type": "string", 33 + "format": "datetime" 34 + }, 35 + "likeCount": { 36 + "type": "integer", 37 + "minimum": 0 38 + } 39 + } 40 + }, 41 + "labelerPolicies": { 42 + "type": "object", 43 + "required": ["labelValues"], 44 + "properties": { 45 + "labelValues": { 46 + "type": "array", 47 + "items": { 48 + "ref": "com.atproto.label.defs#labelValue", 49 + "type": "ref" 50 + }, 51 + "description": "The label values which this labeler publishes. May include global or custom labels." 52 + }, 53 + "labelValueDefinitions": { 54 + "type": "array", 55 + "items": { 56 + "ref": "com.atproto.label.defs#labelValueDefinition", 57 + "type": "ref" 58 + }, 59 + "description": "Label values created by this labeler and scoped exclusively to it. Labels defined here will override global label definitions for this labeler." 60 + } 61 + } 62 + }, 63 + "labelerViewerState": { 64 + "type": "object", 65 + "properties": { 66 + "like": { 67 + "type": "string", 68 + "format": "at-uri" 69 + } 70 + } 71 + }, 72 + "labelerViewDetailed": { 73 + "type": "object", 74 + "required": ["uri", "cid", "creator", "policies", "indexedAt"], 75 + "properties": { 76 + "cid": { 77 + "type": "string", 78 + "format": "cid" 79 + }, 80 + "uri": { 81 + "type": "string", 82 + "format": "at-uri" 83 + }, 84 + "labels": { 85 + "type": "array", 86 + "items": { 87 + "ref": "com.atproto.label.defs#label", 88 + "type": "ref" 89 + } 90 + }, 91 + "viewer": { 92 + "ref": "#labelerViewerState", 93 + "type": "ref" 94 + }, 95 + "creator": { 96 + "ref": "app.bsky.actor.defs#profileView", 97 + "type": "ref" 98 + }, 99 + "policies": { 100 + "ref": "app.bsky.labeler.defs#labelerPolicies", 101 + "type": "ref" 102 + }, 103 + "indexedAt": { 104 + "type": "string", 105 + "format": "datetime" 106 + }, 107 + "likeCount": { 108 + "type": "integer", 109 + "minimum": 0 110 + }, 111 + "reasonTypes": { 112 + "type": "array", 113 + "items": { 114 + "ref": "com.atproto.moderation.defs#reasonType", 115 + "type": "ref" 116 + }, 117 + "description": "The set of report reason 'codes' which are in-scope for this service to review and action. These usually align to policy categories. If not defined (distinct from empty array), all reason types are allowed." 118 + }, 119 + "subjectTypes": { 120 + "type": "array", 121 + "items": { 122 + "ref": "com.atproto.moderation.defs#subjectType", 123 + "type": "ref" 124 + }, 125 + "description": "The set of subject types (account, record, etc) this service accepts reports on." 126 + }, 127 + "subjectCollections": { 128 + "type": "array", 129 + "items": { 130 + "type": "string", 131 + "format": "nsid" 132 + }, 133 + "description": "Set of record types (collection NSIDs) which can be reported to this service. If not defined (distinct from empty array), default is any record type." 134 + } 135 + } 136 + } 137 + }, 138 + "$type": "com.atproto.lexicon.schema", 139 + "lexicon": 1 140 + }
+150 -172
lexicons/app/bsky/notification/defs.json
··· 1 1 { 2 - "id": "app.bsky.notification.defs", 3 - "defs": { 4 - "preference": { 5 - "type": "object", 6 - "required": [ 7 - "list", 8 - "push" 9 - ], 10 - "properties": { 11 - "list": { 12 - "type": "boolean" 13 - }, 14 - "push": { 15 - "type": "boolean" 16 - } 17 - } 18 - }, 19 - "preferences": { 20 - "type": "object", 21 - "required": [ 22 - "chat", 23 - "follow", 24 - "like", 25 - "likeViaRepost", 26 - "mention", 27 - "quote", 28 - "reply", 29 - "repost", 30 - "repostViaRepost", 31 - "starterpackJoined", 32 - "subscribedPost", 33 - "unverified", 34 - "verified" 35 - ], 36 - "properties": { 37 - "chat": { 38 - "ref": "#chatPreference", 39 - "type": "ref" 40 - }, 41 - "like": { 42 - "ref": "#filterablePreference", 43 - "type": "ref" 44 - }, 45 - "quote": { 46 - "ref": "#filterablePreference", 47 - "type": "ref" 48 - }, 49 - "reply": { 50 - "ref": "#filterablePreference", 51 - "type": "ref" 52 - }, 53 - "follow": { 54 - "ref": "#filterablePreference", 55 - "type": "ref" 56 - }, 57 - "repost": { 58 - "ref": "#filterablePreference", 59 - "type": "ref" 60 - }, 61 - "mention": { 62 - "ref": "#filterablePreference", 63 - "type": "ref" 64 - }, 65 - "verified": { 66 - "ref": "#preference", 67 - "type": "ref" 68 - }, 69 - "unverified": { 70 - "ref": "#preference", 71 - "type": "ref" 72 - }, 73 - "likeViaRepost": { 74 - "ref": "#filterablePreference", 75 - "type": "ref" 76 - }, 77 - "subscribedPost": { 78 - "ref": "#preference", 79 - "type": "ref" 80 - }, 81 - "repostViaRepost": { 82 - "ref": "#filterablePreference", 83 - "type": "ref" 84 - }, 85 - "starterpackJoined": { 86 - "ref": "#preference", 87 - "type": "ref" 88 - } 89 - } 90 - }, 91 - "recordDeleted": { 92 - "type": "object", 93 - "properties": {} 94 - }, 95 - "chatPreference": { 96 - "type": "object", 97 - "required": [ 98 - "include", 99 - "push" 100 - ], 101 - "properties": { 102 - "push": { 103 - "type": "boolean" 104 - }, 105 - "include": { 106 - "type": "string", 107 - "knownValues": [ 108 - "all", 109 - "accepted" 110 - ] 111 - } 112 - } 113 - }, 114 - "activitySubscription": { 115 - "type": "object", 116 - "required": [ 117 - "post", 118 - "reply" 119 - ], 120 - "properties": { 121 - "post": { 122 - "type": "boolean" 123 - }, 124 - "reply": { 125 - "type": "boolean" 126 - } 127 - } 128 - }, 129 - "filterablePreference": { 130 - "type": "object", 131 - "required": [ 132 - "include", 133 - "list", 134 - "push" 135 - ], 136 - "properties": { 137 - "list": { 138 - "type": "boolean" 139 - }, 140 - "push": { 141 - "type": "boolean" 142 - }, 143 - "include": { 144 - "type": "string", 145 - "knownValues": [ 146 - "all", 147 - "follows" 148 - ] 149 - } 150 - } 151 - }, 152 - "subjectActivitySubscription": { 153 - "type": "object", 154 - "required": [ 155 - "subject", 156 - "activitySubscription" 157 - ], 158 - "properties": { 159 - "subject": { 160 - "type": "string", 161 - "format": "did" 162 - }, 163 - "activitySubscription": { 164 - "ref": "#activitySubscription", 165 - "type": "ref" 166 - } 167 - }, 168 - "description": "Object used to store activity subscription data in stash." 169 - } 170 - }, 171 - "$type": "com.atproto.lexicon.schema", 172 - "lexicon": 1 173 - } 2 + "id": "app.bsky.notification.defs", 3 + "defs": { 4 + "preference": { 5 + "type": "object", 6 + "required": ["list", "push"], 7 + "properties": { 8 + "list": { 9 + "type": "boolean" 10 + }, 11 + "push": { 12 + "type": "boolean" 13 + } 14 + } 15 + }, 16 + "preferences": { 17 + "type": "object", 18 + "required": [ 19 + "chat", 20 + "follow", 21 + "like", 22 + "likeViaRepost", 23 + "mention", 24 + "quote", 25 + "reply", 26 + "repost", 27 + "repostViaRepost", 28 + "starterpackJoined", 29 + "subscribedPost", 30 + "unverified", 31 + "verified" 32 + ], 33 + "properties": { 34 + "chat": { 35 + "ref": "#chatPreference", 36 + "type": "ref" 37 + }, 38 + "like": { 39 + "ref": "#filterablePreference", 40 + "type": "ref" 41 + }, 42 + "quote": { 43 + "ref": "#filterablePreference", 44 + "type": "ref" 45 + }, 46 + "reply": { 47 + "ref": "#filterablePreference", 48 + "type": "ref" 49 + }, 50 + "follow": { 51 + "ref": "#filterablePreference", 52 + "type": "ref" 53 + }, 54 + "repost": { 55 + "ref": "#filterablePreference", 56 + "type": "ref" 57 + }, 58 + "mention": { 59 + "ref": "#filterablePreference", 60 + "type": "ref" 61 + }, 62 + "verified": { 63 + "ref": "#preference", 64 + "type": "ref" 65 + }, 66 + "unverified": { 67 + "ref": "#preference", 68 + "type": "ref" 69 + }, 70 + "likeViaRepost": { 71 + "ref": "#filterablePreference", 72 + "type": "ref" 73 + }, 74 + "subscribedPost": { 75 + "ref": "#preference", 76 + "type": "ref" 77 + }, 78 + "repostViaRepost": { 79 + "ref": "#filterablePreference", 80 + "type": "ref" 81 + }, 82 + "starterpackJoined": { 83 + "ref": "#preference", 84 + "type": "ref" 85 + } 86 + } 87 + }, 88 + "recordDeleted": { 89 + "type": "object", 90 + "properties": {} 91 + }, 92 + "chatPreference": { 93 + "type": "object", 94 + "required": ["include", "push"], 95 + "properties": { 96 + "push": { 97 + "type": "boolean" 98 + }, 99 + "include": { 100 + "type": "string", 101 + "knownValues": ["all", "accepted"] 102 + } 103 + } 104 + }, 105 + "activitySubscription": { 106 + "type": "object", 107 + "required": ["post", "reply"], 108 + "properties": { 109 + "post": { 110 + "type": "boolean" 111 + }, 112 + "reply": { 113 + "type": "boolean" 114 + } 115 + } 116 + }, 117 + "filterablePreference": { 118 + "type": "object", 119 + "required": ["include", "list", "push"], 120 + "properties": { 121 + "list": { 122 + "type": "boolean" 123 + }, 124 + "push": { 125 + "type": "boolean" 126 + }, 127 + "include": { 128 + "type": "string", 129 + "knownValues": ["all", "follows"] 130 + } 131 + } 132 + }, 133 + "subjectActivitySubscription": { 134 + "type": "object", 135 + "required": ["subject", "activitySubscription"], 136 + "properties": { 137 + "subject": { 138 + "type": "string", 139 + "format": "did" 140 + }, 141 + "activitySubscription": { 142 + "ref": "#activitySubscription", 143 + "type": "ref" 144 + } 145 + }, 146 + "description": "Object used to store activity subscription data in stash." 147 + } 148 + }, 149 + "$type": "com.atproto.lexicon.schema", 150 + "lexicon": 1 151 + }
+73 -89
lexicons/app/bsky/richtext/facet.json
··· 1 1 { 2 - "id": "app.bsky.richtext.facet", 3 - "defs": { 4 - "tag": { 5 - "type": "object", 6 - "required": [ 7 - "tag" 8 - ], 9 - "properties": { 10 - "tag": { 11 - "type": "string", 12 - "maxLength": 640, 13 - "maxGraphemes": 64 14 - } 15 - }, 16 - "description": "Facet feature for a hashtag. The text usually includes a '#' prefix, but the facet reference should not (except in the case of 'double hash tags')." 17 - }, 18 - "link": { 19 - "type": "object", 20 - "required": [ 21 - "uri" 22 - ], 23 - "properties": { 24 - "uri": { 25 - "type": "string", 26 - "format": "uri" 27 - } 28 - }, 29 - "description": "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL." 30 - }, 31 - "main": { 32 - "type": "object", 33 - "required": [ 34 - "index", 35 - "features" 36 - ], 37 - "properties": { 38 - "index": { 39 - "ref": "#byteSlice", 40 - "type": "ref" 41 - }, 42 - "features": { 43 - "type": "array", 44 - "items": { 45 - "refs": [ 46 - "#mention", 47 - "#link", 48 - "#tag" 49 - ], 50 - "type": "union" 51 - } 52 - } 53 - }, 54 - "description": "Annotation of a sub-string within rich text." 55 - }, 56 - "mention": { 57 - "type": "object", 58 - "required": [ 59 - "did" 60 - ], 61 - "properties": { 62 - "did": { 63 - "type": "string", 64 - "format": "did" 65 - } 66 - }, 67 - "description": "Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID." 68 - }, 69 - "byteSlice": { 70 - "type": "object", 71 - "required": [ 72 - "byteStart", 73 - "byteEnd" 74 - ], 75 - "properties": { 76 - "byteEnd": { 77 - "type": "integer", 78 - "minimum": 0 79 - }, 80 - "byteStart": { 81 - "type": "integer", 82 - "minimum": 0 83 - } 84 - }, 85 - "description": "Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets." 86 - } 87 - }, 88 - "$type": "com.atproto.lexicon.schema", 89 - "lexicon": 1 90 - } 2 + "id": "app.bsky.richtext.facet", 3 + "defs": { 4 + "tag": { 5 + "type": "object", 6 + "required": ["tag"], 7 + "properties": { 8 + "tag": { 9 + "type": "string", 10 + "maxLength": 640, 11 + "maxGraphemes": 64 12 + } 13 + }, 14 + "description": "Facet feature for a hashtag. The text usually includes a '#' prefix, but the facet reference should not (except in the case of 'double hash tags')." 15 + }, 16 + "link": { 17 + "type": "object", 18 + "required": ["uri"], 19 + "properties": { 20 + "uri": { 21 + "type": "string", 22 + "format": "uri" 23 + } 24 + }, 25 + "description": "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL." 26 + }, 27 + "main": { 28 + "type": "object", 29 + "required": ["index", "features"], 30 + "properties": { 31 + "index": { 32 + "ref": "#byteSlice", 33 + "type": "ref" 34 + }, 35 + "features": { 36 + "type": "array", 37 + "items": { 38 + "refs": ["#mention", "#link", "#tag"], 39 + "type": "union" 40 + } 41 + } 42 + }, 43 + "description": "Annotation of a sub-string within rich text." 44 + }, 45 + "mention": { 46 + "type": "object", 47 + "required": ["did"], 48 + "properties": { 49 + "did": { 50 + "type": "string", 51 + "format": "did" 52 + } 53 + }, 54 + "description": "Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID." 55 + }, 56 + "byteSlice": { 57 + "type": "object", 58 + "required": ["byteStart", "byteEnd"], 59 + "properties": { 60 + "byteEnd": { 61 + "type": "integer", 62 + "minimum": 0 63 + }, 64 + "byteStart": { 65 + "type": "integer", 66 + "minimum": 0 67 + } 68 + }, 69 + "description": "Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets." 70 + } 71 + }, 72 + "$type": "com.atproto.lexicon.schema", 73 + "lexicon": 1 74 + }
+40 -44
lexicons/com/atproto/identity/resolveHandle.json
··· 1 1 { 2 - "id": "com.atproto.identity.resolveHandle", 3 - "defs": { 4 - "main": { 5 - "type": "query", 6 - "errors": [ 7 - { 8 - "name": "HandleNotFound", 9 - "description": "The resolution process confirmed that the handle does not resolve to any DID." 10 - } 11 - ], 12 - "output": { 13 - "schema": { 14 - "type": "object", 15 - "required": [ 16 - "did" 17 - ], 18 - "properties": { 19 - "did": { 20 - "type": "string", 21 - "format": "did" 22 - } 23 - } 24 - }, 25 - "encoding": "application/json" 26 - }, 27 - "parameters": { 28 - "type": "params", 29 - "required": [ 30 - "handle" 31 - ], 32 - "properties": { 33 - "handle": { 34 - "type": "string", 35 - "format": "handle", 36 - "description": "The handle to resolve." 37 - } 38 - } 39 - }, 40 - "description": "Resolves an atproto handle (hostname) to a DID. Does not necessarily bi-directionally verify against the the DID document." 41 - } 42 - }, 43 - "$type": "com.atproto.lexicon.schema", 44 - "lexicon": 1 45 - } 2 + "id": "com.atproto.identity.resolveHandle", 3 + "defs": { 4 + "main": { 5 + "type": "query", 6 + "errors": [ 7 + { 8 + "name": "HandleNotFound", 9 + "description": "The resolution process confirmed that the handle does not resolve to any DID." 10 + } 11 + ], 12 + "output": { 13 + "schema": { 14 + "type": "object", 15 + "required": ["did"], 16 + "properties": { 17 + "did": { 18 + "type": "string", 19 + "format": "did" 20 + } 21 + } 22 + }, 23 + "encoding": "application/json" 24 + }, 25 + "parameters": { 26 + "type": "params", 27 + "required": ["handle"], 28 + "properties": { 29 + "handle": { 30 + "type": "string", 31 + "format": "handle", 32 + "description": "The handle to resolve." 33 + } 34 + } 35 + }, 36 + "description": "Resolves an atproto handle (hostname) to a DID. Does not necessarily bi-directionally verify against the the DID document." 37 + } 38 + }, 39 + "$type": "com.atproto.lexicon.schema", 40 + "lexicon": 1 41 + }
+162 -192
lexicons/com/atproto/label/defs.json
··· 1 1 { 2 - "id": "com.atproto.label.defs", 3 - "defs": { 4 - "label": { 5 - "type": "object", 6 - "required": [ 7 - "src", 8 - "uri", 9 - "val", 10 - "cts" 11 - ], 12 - "properties": { 13 - "cid": { 14 - "type": "string", 15 - "format": "cid", 16 - "description": "Optionally, CID specifying the specific version of 'uri' resource this label applies to." 17 - }, 18 - "cts": { 19 - "type": "string", 20 - "format": "datetime", 21 - "description": "Timestamp when this label was created." 22 - }, 23 - "exp": { 24 - "type": "string", 25 - "format": "datetime", 26 - "description": "Timestamp at which this label expires (no longer applies)." 27 - }, 28 - "neg": { 29 - "type": "boolean", 30 - "description": "If true, this is a negation label, overwriting a previous label." 31 - }, 32 - "sig": { 33 - "type": "bytes", 34 - "description": "Signature of dag-cbor encoded label." 35 - }, 36 - "src": { 37 - "type": "string", 38 - "format": "did", 39 - "description": "DID of the actor who created this label." 40 - }, 41 - "uri": { 42 - "type": "string", 43 - "format": "uri", 44 - "description": "AT URI of the record, repository (account), or other resource that this label applies to." 45 - }, 46 - "val": { 47 - "type": "string", 48 - "maxLength": 128, 49 - "description": "The short string name of the value or type of this label." 50 - }, 51 - "ver": { 52 - "type": "integer", 53 - "description": "The AT Protocol version of the label object." 54 - } 55 - }, 56 - "description": "Metadata tag on an atproto resource (eg, repo or record)." 57 - }, 58 - "selfLabel": { 59 - "type": "object", 60 - "required": [ 61 - "val" 62 - ], 63 - "properties": { 64 - "val": { 65 - "type": "string", 66 - "maxLength": 128, 67 - "description": "The short string name of the value or type of this label." 68 - } 69 - }, 70 - "description": "Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel." 71 - }, 72 - "labelValue": { 73 - "type": "string", 74 - "knownValues": [ 75 - "!hide", 76 - "!no-promote", 77 - "!warn", 78 - "!no-unauthenticated", 79 - "dmca-violation", 80 - "doxxing", 81 - "porn", 82 - "sexual", 83 - "nudity", 84 - "nsfl", 85 - "gore" 86 - ] 87 - }, 88 - "selfLabels": { 89 - "type": "object", 90 - "required": [ 91 - "values" 92 - ], 93 - "properties": { 94 - "values": { 95 - "type": "array", 96 - "items": { 97 - "ref": "#selfLabel", 98 - "type": "ref" 99 - }, 100 - "maxLength": 10 101 - } 102 - }, 103 - "description": "Metadata tags on an atproto record, published by the author within the record." 104 - }, 105 - "labelValueDefinition": { 106 - "type": "object", 107 - "required": [ 108 - "identifier", 109 - "severity", 110 - "blurs", 111 - "locales" 112 - ], 113 - "properties": { 114 - "blurs": { 115 - "type": "string", 116 - "description": "What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.", 117 - "knownValues": [ 118 - "content", 119 - "media", 120 - "none" 121 - ] 122 - }, 123 - "locales": { 124 - "type": "array", 125 - "items": { 126 - "ref": "#labelValueDefinitionStrings", 127 - "type": "ref" 128 - } 129 - }, 130 - "severity": { 131 - "type": "string", 132 - "description": "How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.", 133 - "knownValues": [ 134 - "inform", 135 - "alert", 136 - "none" 137 - ] 138 - }, 139 - "adultOnly": { 140 - "type": "boolean", 141 - "description": "Does the user need to have adult content enabled in order to configure this label?" 142 - }, 143 - "identifier": { 144 - "type": "string", 145 - "maxLength": 100, 146 - "description": "The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).", 147 - "maxGraphemes": 100 148 - }, 149 - "defaultSetting": { 150 - "type": "string", 151 - "default": "warn", 152 - "description": "The default setting for this label.", 153 - "knownValues": [ 154 - "ignore", 155 - "warn", 156 - "hide" 157 - ] 158 - } 159 - }, 160 - "description": "Declares a label value and its expected interpretations and behaviors." 161 - }, 162 - "labelValueDefinitionStrings": { 163 - "type": "object", 164 - "required": [ 165 - "lang", 166 - "name", 167 - "description" 168 - ], 169 - "properties": { 170 - "lang": { 171 - "type": "string", 172 - "format": "language", 173 - "description": "The code of the language these strings are written in." 174 - }, 175 - "name": { 176 - "type": "string", 177 - "maxLength": 640, 178 - "description": "A short human-readable name for the label.", 179 - "maxGraphemes": 64 180 - }, 181 - "description": { 182 - "type": "string", 183 - "maxLength": 100000, 184 - "description": "A longer description of what the label means and why it might be applied.", 185 - "maxGraphemes": 10000 186 - } 187 - }, 188 - "description": "Strings which describe the label in the UI, localized into a specific language." 189 - } 190 - }, 191 - "$type": "com.atproto.lexicon.schema", 192 - "lexicon": 1 193 - } 2 + "id": "com.atproto.label.defs", 3 + "defs": { 4 + "label": { 5 + "type": "object", 6 + "required": ["src", "uri", "val", "cts"], 7 + "properties": { 8 + "cid": { 9 + "type": "string", 10 + "format": "cid", 11 + "description": "Optionally, CID specifying the specific version of 'uri' resource this label applies to." 12 + }, 13 + "cts": { 14 + "type": "string", 15 + "format": "datetime", 16 + "description": "Timestamp when this label was created." 17 + }, 18 + "exp": { 19 + "type": "string", 20 + "format": "datetime", 21 + "description": "Timestamp at which this label expires (no longer applies)." 22 + }, 23 + "neg": { 24 + "type": "boolean", 25 + "description": "If true, this is a negation label, overwriting a previous label." 26 + }, 27 + "sig": { 28 + "type": "bytes", 29 + "description": "Signature of dag-cbor encoded label." 30 + }, 31 + "src": { 32 + "type": "string", 33 + "format": "did", 34 + "description": "DID of the actor who created this label." 35 + }, 36 + "uri": { 37 + "type": "string", 38 + "format": "uri", 39 + "description": "AT URI of the record, repository (account), or other resource that this label applies to." 40 + }, 41 + "val": { 42 + "type": "string", 43 + "maxLength": 128, 44 + "description": "The short string name of the value or type of this label." 45 + }, 46 + "ver": { 47 + "type": "integer", 48 + "description": "The AT Protocol version of the label object." 49 + } 50 + }, 51 + "description": "Metadata tag on an atproto resource (eg, repo or record)." 52 + }, 53 + "selfLabel": { 54 + "type": "object", 55 + "required": ["val"], 56 + "properties": { 57 + "val": { 58 + "type": "string", 59 + "maxLength": 128, 60 + "description": "The short string name of the value or type of this label." 61 + } 62 + }, 63 + "description": "Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel." 64 + }, 65 + "labelValue": { 66 + "type": "string", 67 + "knownValues": [ 68 + "!hide", 69 + "!no-promote", 70 + "!warn", 71 + "!no-unauthenticated", 72 + "dmca-violation", 73 + "doxxing", 74 + "porn", 75 + "sexual", 76 + "nudity", 77 + "nsfl", 78 + "gore" 79 + ] 80 + }, 81 + "selfLabels": { 82 + "type": "object", 83 + "required": ["values"], 84 + "properties": { 85 + "values": { 86 + "type": "array", 87 + "items": { 88 + "ref": "#selfLabel", 89 + "type": "ref" 90 + }, 91 + "maxLength": 10 92 + } 93 + }, 94 + "description": "Metadata tags on an atproto record, published by the author within the record." 95 + }, 96 + "labelValueDefinition": { 97 + "type": "object", 98 + "required": ["identifier", "severity", "blurs", "locales"], 99 + "properties": { 100 + "blurs": { 101 + "type": "string", 102 + "description": "What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.", 103 + "knownValues": ["content", "media", "none"] 104 + }, 105 + "locales": { 106 + "type": "array", 107 + "items": { 108 + "ref": "#labelValueDefinitionStrings", 109 + "type": "ref" 110 + } 111 + }, 112 + "severity": { 113 + "type": "string", 114 + "description": "How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.", 115 + "knownValues": ["inform", "alert", "none"] 116 + }, 117 + "adultOnly": { 118 + "type": "boolean", 119 + "description": "Does the user need to have adult content enabled in order to configure this label?" 120 + }, 121 + "identifier": { 122 + "type": "string", 123 + "maxLength": 100, 124 + "description": "The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).", 125 + "maxGraphemes": 100 126 + }, 127 + "defaultSetting": { 128 + "type": "string", 129 + "default": "warn", 130 + "description": "The default setting for this label.", 131 + "knownValues": ["ignore", "warn", "hide"] 132 + } 133 + }, 134 + "description": "Declares a label value and its expected interpretations and behaviors." 135 + }, 136 + "labelValueDefinitionStrings": { 137 + "type": "object", 138 + "required": ["lang", "name", "description"], 139 + "properties": { 140 + "lang": { 141 + "type": "string", 142 + "format": "language", 143 + "description": "The code of the language these strings are written in." 144 + }, 145 + "name": { 146 + "type": "string", 147 + "maxLength": 640, 148 + "description": "A short human-readable name for the label.", 149 + "maxGraphemes": 64 150 + }, 151 + "description": { 152 + "type": "string", 153 + "maxLength": 100000, 154 + "description": "A longer description of what the label means and why it might be applied.", 155 + "maxGraphemes": 10000 156 + } 157 + }, 158 + "description": "Strings which describe the label in the UI, localized into a specific language." 159 + } 160 + }, 161 + "$type": "com.atproto.lexicon.schema", 162 + "lexicon": 1 163 + }
+91 -95
lexicons/com/atproto/moderation/defs.json
··· 1 1 { 2 - "id": "com.atproto.moderation.defs", 3 - "defs": { 4 - "reasonRude": { 5 - "type": "token", 6 - "description": "Rude, harassing, explicit, or otherwise unwelcoming behavior. Prefer new lexicon definition `tools.ozone.report.defs#reasonHarassmentOther`." 7 - }, 8 - "reasonSpam": { 9 - "type": "token", 10 - "description": "Spam: frequent unwanted promotion, replies, mentions. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingSpam`." 11 - }, 12 - "reasonType": { 13 - "type": "string", 14 - "knownValues": [ 15 - "com.atproto.moderation.defs#reasonSpam", 16 - "com.atproto.moderation.defs#reasonViolation", 17 - "com.atproto.moderation.defs#reasonMisleading", 18 - "com.atproto.moderation.defs#reasonSexual", 19 - "com.atproto.moderation.defs#reasonRude", 20 - "com.atproto.moderation.defs#reasonOther", 21 - "com.atproto.moderation.defs#reasonAppeal", 22 - "tools.ozone.report.defs#reasonAppeal", 23 - "tools.ozone.report.defs#reasonOther", 24 - "tools.ozone.report.defs#reasonViolenceAnimal", 25 - "tools.ozone.report.defs#reasonViolenceThreats", 26 - "tools.ozone.report.defs#reasonViolenceGraphicContent", 27 - "tools.ozone.report.defs#reasonViolenceGlorification", 28 - "tools.ozone.report.defs#reasonViolenceExtremistContent", 29 - "tools.ozone.report.defs#reasonViolenceTrafficking", 30 - "tools.ozone.report.defs#reasonViolenceOther", 31 - "tools.ozone.report.defs#reasonSexualAbuseContent", 32 - "tools.ozone.report.defs#reasonSexualNCII", 33 - "tools.ozone.report.defs#reasonSexualDeepfake", 34 - "tools.ozone.report.defs#reasonSexualAnimal", 35 - "tools.ozone.report.defs#reasonSexualUnlabeled", 36 - "tools.ozone.report.defs#reasonSexualOther", 37 - "tools.ozone.report.defs#reasonChildSafetyCSAM", 38 - "tools.ozone.report.defs#reasonChildSafetyGroom", 39 - "tools.ozone.report.defs#reasonChildSafetyPrivacy", 40 - "tools.ozone.report.defs#reasonChildSafetyHarassment", 41 - "tools.ozone.report.defs#reasonChildSafetyOther", 42 - "tools.ozone.report.defs#reasonHarassmentTroll", 43 - "tools.ozone.report.defs#reasonHarassmentTargeted", 44 - "tools.ozone.report.defs#reasonHarassmentHateSpeech", 45 - "tools.ozone.report.defs#reasonHarassmentDoxxing", 46 - "tools.ozone.report.defs#reasonHarassmentOther", 47 - "tools.ozone.report.defs#reasonMisleadingBot", 48 - "tools.ozone.report.defs#reasonMisleadingImpersonation", 49 - "tools.ozone.report.defs#reasonMisleadingSpam", 50 - "tools.ozone.report.defs#reasonMisleadingScam", 51 - "tools.ozone.report.defs#reasonMisleadingElections", 52 - "tools.ozone.report.defs#reasonMisleadingOther", 53 - "tools.ozone.report.defs#reasonRuleSiteSecurity", 54 - "tools.ozone.report.defs#reasonRuleProhibitedSales", 55 - "tools.ozone.report.defs#reasonRuleBanEvasion", 56 - "tools.ozone.report.defs#reasonRuleOther", 57 - "tools.ozone.report.defs#reasonSelfHarmContent", 58 - "tools.ozone.report.defs#reasonSelfHarmED", 59 - "tools.ozone.report.defs#reasonSelfHarmStunts", 60 - "tools.ozone.report.defs#reasonSelfHarmSubstances", 61 - "tools.ozone.report.defs#reasonSelfHarmOther" 62 - ] 63 - }, 64 - "reasonOther": { 65 - "type": "token", 66 - "description": "Reports not falling under another report category. Prefer new lexicon definition `tools.ozone.report.defs#reasonOther`." 67 - }, 68 - "subjectType": { 69 - "type": "string", 70 - "description": "Tag describing a type of subject that might be reported.", 71 - "knownValues": [ 72 - "account", 73 - "record", 74 - "chat" 75 - ] 76 - }, 77 - "reasonAppeal": { 78 - "type": "token", 79 - "description": "Appeal a previously taken moderation action" 80 - }, 81 - "reasonSexual": { 82 - "type": "token", 83 - "description": "Unwanted or mislabeled sexual content. Prefer new lexicon definition `tools.ozone.report.defs#reasonSexualUnlabeled`." 84 - }, 85 - "reasonViolation": { 86 - "type": "token", 87 - "description": "Direct violation of server rules, laws, terms of service. Prefer new lexicon definition `tools.ozone.report.defs#reasonRuleOther`." 88 - }, 89 - "reasonMisleading": { 90 - "type": "token", 91 - "description": "Misleading identity, affiliation, or content. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingOther`." 92 - } 93 - }, 94 - "$type": "com.atproto.lexicon.schema", 95 - "lexicon": 1 96 - } 2 + "id": "com.atproto.moderation.defs", 3 + "defs": { 4 + "reasonRude": { 5 + "type": "token", 6 + "description": "Rude, harassing, explicit, or otherwise unwelcoming behavior. Prefer new lexicon definition `tools.ozone.report.defs#reasonHarassmentOther`." 7 + }, 8 + "reasonSpam": { 9 + "type": "token", 10 + "description": "Spam: frequent unwanted promotion, replies, mentions. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingSpam`." 11 + }, 12 + "reasonType": { 13 + "type": "string", 14 + "knownValues": [ 15 + "com.atproto.moderation.defs#reasonSpam", 16 + "com.atproto.moderation.defs#reasonViolation", 17 + "com.atproto.moderation.defs#reasonMisleading", 18 + "com.atproto.moderation.defs#reasonSexual", 19 + "com.atproto.moderation.defs#reasonRude", 20 + "com.atproto.moderation.defs#reasonOther", 21 + "com.atproto.moderation.defs#reasonAppeal", 22 + "tools.ozone.report.defs#reasonAppeal", 23 + "tools.ozone.report.defs#reasonOther", 24 + "tools.ozone.report.defs#reasonViolenceAnimal", 25 + "tools.ozone.report.defs#reasonViolenceThreats", 26 + "tools.ozone.report.defs#reasonViolenceGraphicContent", 27 + "tools.ozone.report.defs#reasonViolenceGlorification", 28 + "tools.ozone.report.defs#reasonViolenceExtremistContent", 29 + "tools.ozone.report.defs#reasonViolenceTrafficking", 30 + "tools.ozone.report.defs#reasonViolenceOther", 31 + "tools.ozone.report.defs#reasonSexualAbuseContent", 32 + "tools.ozone.report.defs#reasonSexualNCII", 33 + "tools.ozone.report.defs#reasonSexualDeepfake", 34 + "tools.ozone.report.defs#reasonSexualAnimal", 35 + "tools.ozone.report.defs#reasonSexualUnlabeled", 36 + "tools.ozone.report.defs#reasonSexualOther", 37 + "tools.ozone.report.defs#reasonChildSafetyCSAM", 38 + "tools.ozone.report.defs#reasonChildSafetyGroom", 39 + "tools.ozone.report.defs#reasonChildSafetyPrivacy", 40 + "tools.ozone.report.defs#reasonChildSafetyHarassment", 41 + "tools.ozone.report.defs#reasonChildSafetyOther", 42 + "tools.ozone.report.defs#reasonHarassmentTroll", 43 + "tools.ozone.report.defs#reasonHarassmentTargeted", 44 + "tools.ozone.report.defs#reasonHarassmentHateSpeech", 45 + "tools.ozone.report.defs#reasonHarassmentDoxxing", 46 + "tools.ozone.report.defs#reasonHarassmentOther", 47 + "tools.ozone.report.defs#reasonMisleadingBot", 48 + "tools.ozone.report.defs#reasonMisleadingImpersonation", 49 + "tools.ozone.report.defs#reasonMisleadingSpam", 50 + "tools.ozone.report.defs#reasonMisleadingScam", 51 + "tools.ozone.report.defs#reasonMisleadingElections", 52 + "tools.ozone.report.defs#reasonMisleadingOther", 53 + "tools.ozone.report.defs#reasonRuleSiteSecurity", 54 + "tools.ozone.report.defs#reasonRuleProhibitedSales", 55 + "tools.ozone.report.defs#reasonRuleBanEvasion", 56 + "tools.ozone.report.defs#reasonRuleOther", 57 + "tools.ozone.report.defs#reasonSelfHarmContent", 58 + "tools.ozone.report.defs#reasonSelfHarmED", 59 + "tools.ozone.report.defs#reasonSelfHarmStunts", 60 + "tools.ozone.report.defs#reasonSelfHarmSubstances", 61 + "tools.ozone.report.defs#reasonSelfHarmOther" 62 + ] 63 + }, 64 + "reasonOther": { 65 + "type": "token", 66 + "description": "Reports not falling under another report category. Prefer new lexicon definition `tools.ozone.report.defs#reasonOther`." 67 + }, 68 + "subjectType": { 69 + "type": "string", 70 + "description": "Tag describing a type of subject that might be reported.", 71 + "knownValues": ["account", "record", "chat"] 72 + }, 73 + "reasonAppeal": { 74 + "type": "token", 75 + "description": "Appeal a previously taken moderation action" 76 + }, 77 + "reasonSexual": { 78 + "type": "token", 79 + "description": "Unwanted or mislabeled sexual content. Prefer new lexicon definition `tools.ozone.report.defs#reasonSexualUnlabeled`." 80 + }, 81 + "reasonViolation": { 82 + "type": "token", 83 + "description": "Direct violation of server rules, laws, terms of service. Prefer new lexicon definition `tools.ozone.report.defs#reasonRuleOther`." 84 + }, 85 + "reasonMisleading": { 86 + "type": "token", 87 + "description": "Misleading identity, affiliation, or content. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingOther`." 88 + } 89 + }, 90 + "$type": "com.atproto.lexicon.schema", 91 + "lexicon": 1 92 + }
+162 -195
lexicons/com/atproto/repo/applyWrites.json
··· 1 1 { 2 - "id": "com.atproto.repo.applyWrites", 3 - "defs": { 4 - "main": { 5 - "type": "procedure", 6 - "input": { 7 - "schema": { 8 - "type": "object", 9 - "required": [ 10 - "repo", 11 - "writes" 12 - ], 13 - "properties": { 14 - "repo": { 15 - "type": "string", 16 - "format": "at-identifier", 17 - "description": "The handle or DID of the repo (aka, current account)." 18 - }, 19 - "writes": { 20 - "type": "array", 21 - "items": { 22 - "refs": [ 23 - "#create", 24 - "#update", 25 - "#delete" 26 - ], 27 - "type": "union", 28 - "closed": true 29 - } 30 - }, 31 - "validate": { 32 - "type": "boolean", 33 - "description": "Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons." 34 - }, 35 - "swapCommit": { 36 - "type": "string", 37 - "format": "cid", 38 - "description": "If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations." 39 - } 40 - } 41 - }, 42 - "encoding": "application/json" 43 - }, 44 - "errors": [ 45 - { 46 - "name": "InvalidSwap", 47 - "description": "Indicates that the 'swapCommit' parameter did not match current commit." 48 - } 49 - ], 50 - "output": { 51 - "schema": { 52 - "type": "object", 53 - "required": [], 54 - "properties": { 55 - "commit": { 56 - "ref": "com.atproto.repo.defs#commitMeta", 57 - "type": "ref" 58 - }, 59 - "results": { 60 - "type": "array", 61 - "items": { 62 - "refs": [ 63 - "#createResult", 64 - "#updateResult", 65 - "#deleteResult" 66 - ], 67 - "type": "union", 68 - "closed": true 69 - } 70 - } 71 - } 72 - }, 73 - "encoding": "application/json" 74 - }, 75 - "description": "Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS." 76 - }, 77 - "create": { 78 - "type": "object", 79 - "required": [ 80 - "collection", 81 - "value" 82 - ], 83 - "properties": { 84 - "rkey": { 85 - "type": "string", 86 - "format": "record-key", 87 - "maxLength": 512, 88 - "description": "NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility." 89 - }, 90 - "value": { 91 - "type": "unknown" 92 - }, 93 - "collection": { 94 - "type": "string", 95 - "format": "nsid" 96 - } 97 - }, 98 - "description": "Operation which creates a new record." 99 - }, 100 - "delete": { 101 - "type": "object", 102 - "required": [ 103 - "collection", 104 - "rkey" 105 - ], 106 - "properties": { 107 - "rkey": { 108 - "type": "string", 109 - "format": "record-key" 110 - }, 111 - "collection": { 112 - "type": "string", 113 - "format": "nsid" 114 - } 115 - }, 116 - "description": "Operation which deletes an existing record." 117 - }, 118 - "update": { 119 - "type": "object", 120 - "required": [ 121 - "collection", 122 - "rkey", 123 - "value" 124 - ], 125 - "properties": { 126 - "rkey": { 127 - "type": "string", 128 - "format": "record-key" 129 - }, 130 - "value": { 131 - "type": "unknown" 132 - }, 133 - "collection": { 134 - "type": "string", 135 - "format": "nsid" 136 - } 137 - }, 138 - "description": "Operation which updates an existing record." 139 - }, 140 - "createResult": { 141 - "type": "object", 142 - "required": [ 143 - "uri", 144 - "cid" 145 - ], 146 - "properties": { 147 - "cid": { 148 - "type": "string", 149 - "format": "cid" 150 - }, 151 - "uri": { 152 - "type": "string", 153 - "format": "at-uri" 154 - }, 155 - "validationStatus": { 156 - "type": "string", 157 - "knownValues": [ 158 - "valid", 159 - "unknown" 160 - ] 161 - } 162 - } 163 - }, 164 - "deleteResult": { 165 - "type": "object", 166 - "required": [], 167 - "properties": {} 168 - }, 169 - "updateResult": { 170 - "type": "object", 171 - "required": [ 172 - "uri", 173 - "cid" 174 - ], 175 - "properties": { 176 - "cid": { 177 - "type": "string", 178 - "format": "cid" 179 - }, 180 - "uri": { 181 - "type": "string", 182 - "format": "at-uri" 183 - }, 184 - "validationStatus": { 185 - "type": "string", 186 - "knownValues": [ 187 - "valid", 188 - "unknown" 189 - ] 190 - } 191 - } 192 - } 193 - }, 194 - "$type": "com.atproto.lexicon.schema", 195 - "lexicon": 1 196 - } 2 + "id": "com.atproto.repo.applyWrites", 3 + "defs": { 4 + "main": { 5 + "type": "procedure", 6 + "input": { 7 + "schema": { 8 + "type": "object", 9 + "required": ["repo", "writes"], 10 + "properties": { 11 + "repo": { 12 + "type": "string", 13 + "format": "at-identifier", 14 + "description": "The handle or DID of the repo (aka, current account)." 15 + }, 16 + "writes": { 17 + "type": "array", 18 + "items": { 19 + "refs": ["#create", "#update", "#delete"], 20 + "type": "union", 21 + "closed": true 22 + } 23 + }, 24 + "validate": { 25 + "type": "boolean", 26 + "description": "Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons." 27 + }, 28 + "swapCommit": { 29 + "type": "string", 30 + "format": "cid", 31 + "description": "If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations." 32 + } 33 + } 34 + }, 35 + "encoding": "application/json" 36 + }, 37 + "errors": [ 38 + { 39 + "name": "InvalidSwap", 40 + "description": "Indicates that the 'swapCommit' parameter did not match current commit." 41 + } 42 + ], 43 + "output": { 44 + "schema": { 45 + "type": "object", 46 + "required": [], 47 + "properties": { 48 + "commit": { 49 + "ref": "com.atproto.repo.defs#commitMeta", 50 + "type": "ref" 51 + }, 52 + "results": { 53 + "type": "array", 54 + "items": { 55 + "refs": ["#createResult", "#updateResult", "#deleteResult"], 56 + "type": "union", 57 + "closed": true 58 + } 59 + } 60 + } 61 + }, 62 + "encoding": "application/json" 63 + }, 64 + "description": "Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS." 65 + }, 66 + "create": { 67 + "type": "object", 68 + "required": ["collection", "value"], 69 + "properties": { 70 + "rkey": { 71 + "type": "string", 72 + "format": "record-key", 73 + "maxLength": 512, 74 + "description": "NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility." 75 + }, 76 + "value": { 77 + "type": "unknown" 78 + }, 79 + "collection": { 80 + "type": "string", 81 + "format": "nsid" 82 + } 83 + }, 84 + "description": "Operation which creates a new record." 85 + }, 86 + "delete": { 87 + "type": "object", 88 + "required": ["collection", "rkey"], 89 + "properties": { 90 + "rkey": { 91 + "type": "string", 92 + "format": "record-key" 93 + }, 94 + "collection": { 95 + "type": "string", 96 + "format": "nsid" 97 + } 98 + }, 99 + "description": "Operation which deletes an existing record." 100 + }, 101 + "update": { 102 + "type": "object", 103 + "required": ["collection", "rkey", "value"], 104 + "properties": { 105 + "rkey": { 106 + "type": "string", 107 + "format": "record-key" 108 + }, 109 + "value": { 110 + "type": "unknown" 111 + }, 112 + "collection": { 113 + "type": "string", 114 + "format": "nsid" 115 + } 116 + }, 117 + "description": "Operation which updates an existing record." 118 + }, 119 + "createResult": { 120 + "type": "object", 121 + "required": ["uri", "cid"], 122 + "properties": { 123 + "cid": { 124 + "type": "string", 125 + "format": "cid" 126 + }, 127 + "uri": { 128 + "type": "string", 129 + "format": "at-uri" 130 + }, 131 + "validationStatus": { 132 + "type": "string", 133 + "knownValues": ["valid", "unknown"] 134 + } 135 + } 136 + }, 137 + "deleteResult": { 138 + "type": "object", 139 + "required": [], 140 + "properties": {} 141 + }, 142 + "updateResult": { 143 + "type": "object", 144 + "required": ["uri", "cid"], 145 + "properties": { 146 + "cid": { 147 + "type": "string", 148 + "format": "cid" 149 + }, 150 + "uri": { 151 + "type": "string", 152 + "format": "at-uri" 153 + }, 154 + "validationStatus": { 155 + "type": "string", 156 + "knownValues": ["valid", "unknown"] 157 + } 158 + } 159 + } 160 + }, 161 + "$type": "com.atproto.lexicon.schema", 162 + "lexicon": 1 163 + }
+79 -89
lexicons/com/atproto/repo/createRecord.json
··· 1 1 { 2 - "id": "com.atproto.repo.createRecord", 3 - "defs": { 4 - "main": { 5 - "type": "procedure", 6 - "input": { 7 - "schema": { 8 - "type": "object", 9 - "required": [ 10 - "repo", 11 - "collection", 12 - "record" 13 - ], 14 - "properties": { 15 - "repo": { 16 - "type": "string", 17 - "format": "at-identifier", 18 - "description": "The handle or DID of the repo (aka, current account)." 19 - }, 20 - "rkey": { 21 - "type": "string", 22 - "format": "record-key", 23 - "maxLength": 512, 24 - "description": "The Record Key." 25 - }, 26 - "record": { 27 - "type": "unknown", 28 - "description": "The record itself. Must contain a $type field." 29 - }, 30 - "validate": { 31 - "type": "boolean", 32 - "description": "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons." 33 - }, 34 - "collection": { 35 - "type": "string", 36 - "format": "nsid", 37 - "description": "The NSID of the record collection." 38 - }, 39 - "swapCommit": { 40 - "type": "string", 41 - "format": "cid", 42 - "description": "Compare and swap with the previous commit by CID." 43 - } 44 - } 45 - }, 46 - "encoding": "application/json" 47 - }, 48 - "errors": [ 49 - { 50 - "name": "InvalidSwap", 51 - "description": "Indicates that 'swapCommit' didn't match current repo commit." 52 - } 53 - ], 54 - "output": { 55 - "schema": { 56 - "type": "object", 57 - "required": [ 58 - "uri", 59 - "cid" 60 - ], 61 - "properties": { 62 - "cid": { 63 - "type": "string", 64 - "format": "cid" 65 - }, 66 - "uri": { 67 - "type": "string", 68 - "format": "at-uri" 69 - }, 70 - "commit": { 71 - "ref": "com.atproto.repo.defs#commitMeta", 72 - "type": "ref" 73 - }, 74 - "validationStatus": { 75 - "type": "string", 76 - "knownValues": [ 77 - "valid", 78 - "unknown" 79 - ] 80 - } 81 - } 82 - }, 83 - "encoding": "application/json" 84 - }, 85 - "description": "Create a single new repository record. Requires auth, implemented by PDS." 86 - } 87 - }, 88 - "$type": "com.atproto.lexicon.schema", 89 - "lexicon": 1 90 - } 2 + "id": "com.atproto.repo.createRecord", 3 + "defs": { 4 + "main": { 5 + "type": "procedure", 6 + "input": { 7 + "schema": { 8 + "type": "object", 9 + "required": ["repo", "collection", "record"], 10 + "properties": { 11 + "repo": { 12 + "type": "string", 13 + "format": "at-identifier", 14 + "description": "The handle or DID of the repo (aka, current account)." 15 + }, 16 + "rkey": { 17 + "type": "string", 18 + "format": "record-key", 19 + "maxLength": 512, 20 + "description": "The Record Key." 21 + }, 22 + "record": { 23 + "type": "unknown", 24 + "description": "The record itself. Must contain a $type field." 25 + }, 26 + "validate": { 27 + "type": "boolean", 28 + "description": "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons." 29 + }, 30 + "collection": { 31 + "type": "string", 32 + "format": "nsid", 33 + "description": "The NSID of the record collection." 34 + }, 35 + "swapCommit": { 36 + "type": "string", 37 + "format": "cid", 38 + "description": "Compare and swap with the previous commit by CID." 39 + } 40 + } 41 + }, 42 + "encoding": "application/json" 43 + }, 44 + "errors": [ 45 + { 46 + "name": "InvalidSwap", 47 + "description": "Indicates that 'swapCommit' didn't match current repo commit." 48 + } 49 + ], 50 + "output": { 51 + "schema": { 52 + "type": "object", 53 + "required": ["uri", "cid"], 54 + "properties": { 55 + "cid": { 56 + "type": "string", 57 + "format": "cid" 58 + }, 59 + "uri": { 60 + "type": "string", 61 + "format": "at-uri" 62 + }, 63 + "commit": { 64 + "ref": "com.atproto.repo.defs#commitMeta", 65 + "type": "ref" 66 + }, 67 + "validationStatus": { 68 + "type": "string", 69 + "knownValues": ["valid", "unknown"] 70 + } 71 + } 72 + }, 73 + "encoding": "application/json" 74 + }, 75 + "description": "Create a single new repository record. Requires auth, implemented by PDS." 76 + } 77 + }, 78 + "$type": "com.atproto.lexicon.schema", 79 + "lexicon": 1 80 + }
+20 -23
lexicons/com/atproto/repo/defs.json
··· 1 1 { 2 - "id": "com.atproto.repo.defs", 3 - "defs": { 4 - "commitMeta": { 5 - "type": "object", 6 - "required": [ 7 - "cid", 8 - "rev" 9 - ], 10 - "properties": { 11 - "cid": { 12 - "type": "string", 13 - "format": "cid" 14 - }, 15 - "rev": { 16 - "type": "string", 17 - "format": "tid" 18 - } 19 - } 20 - } 21 - }, 22 - "$type": "com.atproto.lexicon.schema", 23 - "lexicon": 1 24 - } 2 + "id": "com.atproto.repo.defs", 3 + "defs": { 4 + "commitMeta": { 5 + "type": "object", 6 + "required": ["cid", "rev"], 7 + "properties": { 8 + "cid": { 9 + "type": "string", 10 + "format": "cid" 11 + }, 12 + "rev": { 13 + "type": "string", 14 + "format": "tid" 15 + } 16 + } 17 + } 18 + }, 19 + "$type": "com.atproto.lexicon.schema", 20 + "lexicon": 1 21 + }
+61 -65
lexicons/com/atproto/repo/deleteRecord.json
··· 1 1 { 2 - "id": "com.atproto.repo.deleteRecord", 3 - "defs": { 4 - "main": { 5 - "type": "procedure", 6 - "input": { 7 - "schema": { 8 - "type": "object", 9 - "required": [ 10 - "repo", 11 - "collection", 12 - "rkey" 13 - ], 14 - "properties": { 15 - "repo": { 16 - "type": "string", 17 - "format": "at-identifier", 18 - "description": "The handle or DID of the repo (aka, current account)." 19 - }, 20 - "rkey": { 21 - "type": "string", 22 - "format": "record-key", 23 - "description": "The Record Key." 24 - }, 25 - "collection": { 26 - "type": "string", 27 - "format": "nsid", 28 - "description": "The NSID of the record collection." 29 - }, 30 - "swapCommit": { 31 - "type": "string", 32 - "format": "cid", 33 - "description": "Compare and swap with the previous commit by CID." 34 - }, 35 - "swapRecord": { 36 - "type": "string", 37 - "format": "cid", 38 - "description": "Compare and swap with the previous record by CID." 39 - } 40 - } 41 - }, 42 - "encoding": "application/json" 43 - }, 44 - "errors": [ 45 - { 46 - "name": "InvalidSwap" 47 - } 48 - ], 49 - "output": { 50 - "schema": { 51 - "type": "object", 52 - "properties": { 53 - "commit": { 54 - "ref": "com.atproto.repo.defs#commitMeta", 55 - "type": "ref" 56 - } 57 - } 58 - }, 59 - "encoding": "application/json" 60 - }, 61 - "description": "Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS." 62 - } 63 - }, 64 - "$type": "com.atproto.lexicon.schema", 65 - "lexicon": 1 66 - } 2 + "id": "com.atproto.repo.deleteRecord", 3 + "defs": { 4 + "main": { 5 + "type": "procedure", 6 + "input": { 7 + "schema": { 8 + "type": "object", 9 + "required": ["repo", "collection", "rkey"], 10 + "properties": { 11 + "repo": { 12 + "type": "string", 13 + "format": "at-identifier", 14 + "description": "The handle or DID of the repo (aka, current account)." 15 + }, 16 + "rkey": { 17 + "type": "string", 18 + "format": "record-key", 19 + "description": "The Record Key." 20 + }, 21 + "collection": { 22 + "type": "string", 23 + "format": "nsid", 24 + "description": "The NSID of the record collection." 25 + }, 26 + "swapCommit": { 27 + "type": "string", 28 + "format": "cid", 29 + "description": "Compare and swap with the previous commit by CID." 30 + }, 31 + "swapRecord": { 32 + "type": "string", 33 + "format": "cid", 34 + "description": "Compare and swap with the previous record by CID." 35 + } 36 + } 37 + }, 38 + "encoding": "application/json" 39 + }, 40 + "errors": [ 41 + { 42 + "name": "InvalidSwap" 43 + } 44 + ], 45 + "output": { 46 + "schema": { 47 + "type": "object", 48 + "properties": { 49 + "commit": { 50 + "ref": "com.atproto.repo.defs#commitMeta", 51 + "type": "ref" 52 + } 53 + } 54 + }, 55 + "encoding": "application/json" 56 + }, 57 + "description": "Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS." 58 + } 59 + }, 60 + "$type": "com.atproto.lexicon.schema", 61 + "lexicon": 1 62 + }
+61 -68
lexicons/com/atproto/repo/getRecord.json
··· 1 1 { 2 - "id": "com.atproto.repo.getRecord", 3 - "defs": { 4 - "main": { 5 - "type": "query", 6 - "errors": [ 7 - { 8 - "name": "RecordNotFound" 9 - } 10 - ], 11 - "output": { 12 - "schema": { 13 - "type": "object", 14 - "required": [ 15 - "uri", 16 - "value" 17 - ], 18 - "properties": { 19 - "cid": { 20 - "type": "string", 21 - "format": "cid" 22 - }, 23 - "uri": { 24 - "type": "string", 25 - "format": "at-uri" 26 - }, 27 - "value": { 28 - "type": "unknown" 29 - } 30 - } 31 - }, 32 - "encoding": "application/json" 33 - }, 34 - "parameters": { 35 - "type": "params", 36 - "required": [ 37 - "repo", 38 - "collection", 39 - "rkey" 40 - ], 41 - "properties": { 42 - "cid": { 43 - "type": "string", 44 - "format": "cid", 45 - "description": "The CID of the version of the record. If not specified, then return the most recent version." 46 - }, 47 - "repo": { 48 - "type": "string", 49 - "format": "at-identifier", 50 - "description": "The handle or DID of the repo." 51 - }, 52 - "rkey": { 53 - "type": "string", 54 - "format": "record-key", 55 - "description": "The Record Key." 56 - }, 57 - "collection": { 58 - "type": "string", 59 - "format": "nsid", 60 - "description": "The NSID of the record collection." 61 - } 62 - } 63 - }, 64 - "description": "Get a single record from a repository. Does not require auth." 65 - } 66 - }, 67 - "$type": "com.atproto.lexicon.schema", 68 - "lexicon": 1 69 - } 2 + "id": "com.atproto.repo.getRecord", 3 + "defs": { 4 + "main": { 5 + "type": "query", 6 + "errors": [ 7 + { 8 + "name": "RecordNotFound" 9 + } 10 + ], 11 + "output": { 12 + "schema": { 13 + "type": "object", 14 + "required": ["uri", "value"], 15 + "properties": { 16 + "cid": { 17 + "type": "string", 18 + "format": "cid" 19 + }, 20 + "uri": { 21 + "type": "string", 22 + "format": "at-uri" 23 + }, 24 + "value": { 25 + "type": "unknown" 26 + } 27 + } 28 + }, 29 + "encoding": "application/json" 30 + }, 31 + "parameters": { 32 + "type": "params", 33 + "required": ["repo", "collection", "rkey"], 34 + "properties": { 35 + "cid": { 36 + "type": "string", 37 + "format": "cid", 38 + "description": "The CID of the version of the record. If not specified, then return the most recent version." 39 + }, 40 + "repo": { 41 + "type": "string", 42 + "format": "at-identifier", 43 + "description": "The handle or DID of the repo." 44 + }, 45 + "rkey": { 46 + "type": "string", 47 + "format": "record-key", 48 + "description": "The Record Key." 49 + }, 50 + "collection": { 51 + "type": "string", 52 + "format": "nsid", 53 + "description": "The NSID of the record collection." 54 + } 55 + } 56 + }, 57 + "description": "Get a single record from a repository. Does not require auth." 58 + } 59 + }, 60 + "$type": "com.atproto.lexicon.schema", 61 + "lexicon": 1 62 + }
+76 -85
lexicons/com/atproto/repo/listRecords.json
··· 1 1 { 2 - "id": "com.atproto.repo.listRecords", 3 - "defs": { 4 - "main": { 5 - "type": "query", 6 - "output": { 7 - "schema": { 8 - "type": "object", 9 - "required": [ 10 - "records" 11 - ], 12 - "properties": { 13 - "cursor": { 14 - "type": "string" 15 - }, 16 - "records": { 17 - "type": "array", 18 - "items": { 19 - "ref": "#record", 20 - "type": "ref" 21 - } 22 - } 23 - } 24 - }, 25 - "encoding": "application/json" 26 - }, 27 - "parameters": { 28 - "type": "params", 29 - "required": [ 30 - "repo", 31 - "collection" 32 - ], 33 - "properties": { 34 - "repo": { 35 - "type": "string", 36 - "format": "at-identifier", 37 - "description": "The handle or DID of the repo." 38 - }, 39 - "limit": { 40 - "type": "integer", 41 - "default": 50, 42 - "maximum": 100, 43 - "minimum": 1, 44 - "description": "The number of records to return." 45 - }, 46 - "cursor": { 47 - "type": "string" 48 - }, 49 - "reverse": { 50 - "type": "boolean", 51 - "description": "Flag to reverse the order of the returned records." 52 - }, 53 - "collection": { 54 - "type": "string", 55 - "format": "nsid", 56 - "description": "The NSID of the record type." 57 - } 58 - } 59 - }, 60 - "description": "List a range of records in a repository, matching a specific collection. Does not require auth." 61 - }, 62 - "record": { 63 - "type": "object", 64 - "required": [ 65 - "uri", 66 - "cid", 67 - "value" 68 - ], 69 - "properties": { 70 - "cid": { 71 - "type": "string", 72 - "format": "cid" 73 - }, 74 - "uri": { 75 - "type": "string", 76 - "format": "at-uri" 77 - }, 78 - "value": { 79 - "type": "unknown" 80 - } 81 - } 82 - } 83 - }, 84 - "$type": "com.atproto.lexicon.schema", 85 - "lexicon": 1 86 - } 2 + "id": "com.atproto.repo.listRecords", 3 + "defs": { 4 + "main": { 5 + "type": "query", 6 + "output": { 7 + "schema": { 8 + "type": "object", 9 + "required": ["records"], 10 + "properties": { 11 + "cursor": { 12 + "type": "string" 13 + }, 14 + "records": { 15 + "type": "array", 16 + "items": { 17 + "ref": "#record", 18 + "type": "ref" 19 + } 20 + } 21 + } 22 + }, 23 + "encoding": "application/json" 24 + }, 25 + "parameters": { 26 + "type": "params", 27 + "required": ["repo", "collection"], 28 + "properties": { 29 + "repo": { 30 + "type": "string", 31 + "format": "at-identifier", 32 + "description": "The handle or DID of the repo." 33 + }, 34 + "limit": { 35 + "type": "integer", 36 + "default": 50, 37 + "maximum": 100, 38 + "minimum": 1, 39 + "description": "The number of records to return." 40 + }, 41 + "cursor": { 42 + "type": "string" 43 + }, 44 + "reverse": { 45 + "type": "boolean", 46 + "description": "Flag to reverse the order of the returned records." 47 + }, 48 + "collection": { 49 + "type": "string", 50 + "format": "nsid", 51 + "description": "The NSID of the record type." 52 + } 53 + } 54 + }, 55 + "description": "List a range of records in a repository, matching a specific collection. Does not require auth." 56 + }, 57 + "record": { 58 + "type": "object", 59 + "required": ["uri", "cid", "value"], 60 + "properties": { 61 + "cid": { 62 + "type": "string", 63 + "format": "cid" 64 + }, 65 + "uri": { 66 + "type": "string", 67 + "format": "at-uri" 68 + }, 69 + "value": { 70 + "type": "unknown" 71 + } 72 + } 73 + } 74 + }, 75 + "$type": "com.atproto.lexicon.schema", 76 + "lexicon": 1 77 + }
+21 -24
lexicons/com/atproto/repo/strongRef.json
··· 1 1 { 2 - "id": "com.atproto.repo.strongRef", 3 - "defs": { 4 - "main": { 5 - "type": "object", 6 - "required": [ 7 - "uri", 8 - "cid" 9 - ], 10 - "properties": { 11 - "cid": { 12 - "type": "string", 13 - "format": "cid" 14 - }, 15 - "uri": { 16 - "type": "string", 17 - "format": "at-uri" 18 - } 19 - } 20 - } 21 - }, 22 - "$type": "com.atproto.lexicon.schema", 23 - "lexicon": 1, 24 - "description": "A URI with a content-hash fingerprint." 25 - } 2 + "id": "com.atproto.repo.strongRef", 3 + "defs": { 4 + "main": { 5 + "type": "object", 6 + "required": ["uri", "cid"], 7 + "properties": { 8 + "cid": { 9 + "type": "string", 10 + "format": "cid" 11 + }, 12 + "uri": { 13 + "type": "string", 14 + "format": "at-uri" 15 + } 16 + } 17 + } 18 + }, 19 + "$type": "com.atproto.lexicon.schema", 20 + "lexicon": 1, 21 + "description": "A URI with a content-hash fingerprint." 22 + }
+35 -35
lexicons/one/papili/comment.json
··· 1 1 { 2 - "lexicon": 1, 3 - "id": "one.papili.comment", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "tid", 8 - "description": "A comment on a papili.one post or another comment", 9 - "record": { 10 - "type": "object", 11 - "required": ["post", "text", "createdAt"], 12 - "properties": { 13 - "post": { 14 - "type": "ref", 15 - "ref": "com.atproto.repo.strongRef", 16 - "description": "Reference to the root post (uri + cid)" 17 - }, 18 - "parent": { 19 - "type": "ref", 20 - "ref": "com.atproto.repo.strongRef", 21 - "description": "Reference to parent comment if this is a reply" 22 - }, 23 - "text": { 24 - "type": "string", 25 - "maxLength": 10000, 26 - "description": "The comment text" 27 - }, 28 - "createdAt": { 29 - "type": "string", 30 - "format": "datetime", 31 - "description": "Timestamp of creation" 32 - } 33 - } 34 - } 35 - } 36 - } 2 + "lexicon": 1, 3 + "id": "one.papili.comment", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "description": "A comment on a papili.one post or another comment", 9 + "record": { 10 + "type": "object", 11 + "required": ["post", "text", "createdAt"], 12 + "properties": { 13 + "post": { 14 + "type": "ref", 15 + "ref": "com.atproto.repo.strongRef", 16 + "description": "Reference to the root post (uri + cid)" 17 + }, 18 + "parent": { 19 + "type": "ref", 20 + "ref": "com.atproto.repo.strongRef", 21 + "description": "Reference to parent comment if this is a reply" 22 + }, 23 + "text": { 24 + "type": "string", 25 + "maxLength": 10000, 26 + "description": "The comment text" 27 + }, 28 + "createdAt": { 29 + "type": "string", 30 + "format": "datetime", 31 + "description": "Timestamp of creation" 32 + } 33 + } 34 + } 35 + } 36 + } 37 37 }
+35 -35
lexicons/one/papili/post.json
··· 1 1 { 2 - "lexicon": 1, 3 - "id": "one.papili.post", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "tid", 8 - "description": "A post on papili.one - either a link submission or text post", 9 - "record": { 10 - "type": "object", 11 - "required": ["title", "createdAt"], 12 - "properties": { 13 - "url": { 14 - "type": "string", 15 - "format": "uri", 16 - "description": "Optional URL for link submissions" 17 - }, 18 - "title": { 19 - "type": "string", 20 - "maxLength": 300, 21 - "description": "Title of the submission" 22 - }, 23 - "text": { 24 - "type": "string", 25 - "maxLength": 10000, 26 - "description": "Optional description or commentary" 27 - }, 28 - "createdAt": { 29 - "type": "string", 30 - "format": "datetime", 31 - "description": "Timestamp of creation" 32 - } 33 - } 34 - } 35 - } 36 - } 2 + "lexicon": 1, 3 + "id": "one.papili.post", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "description": "A post on papili.one - either a link submission or text post", 9 + "record": { 10 + "type": "object", 11 + "required": ["title", "createdAt"], 12 + "properties": { 13 + "url": { 14 + "type": "string", 15 + "format": "uri", 16 + "description": "Optional URL for link submissions" 17 + }, 18 + "title": { 19 + "type": "string", 20 + "maxLength": 300, 21 + "description": "Title of the submission" 22 + }, 23 + "text": { 24 + "type": "string", 25 + "maxLength": 10000, 26 + "description": "Optional description or commentary" 27 + }, 28 + "createdAt": { 29 + "type": "string", 30 + "format": "datetime", 31 + "description": "Timestamp of creation" 32 + } 33 + } 34 + } 35 + } 36 + } 37 37 }
+211 -211
lexicons/tools/ozone/report/defs.json
··· 1 1 { 2 - "id": "tools.ozone.report.defs", 3 - "defs": { 4 - "reasonType": { 5 - "type": "string", 6 - "knownValues": [ 7 - "tools.ozone.report.defs#reasonAppeal", 8 - "tools.ozone.report.defs#reasonOther", 9 - "tools.ozone.report.defs#reasonViolenceAnimal", 10 - "tools.ozone.report.defs#reasonViolenceThreats", 11 - "tools.ozone.report.defs#reasonViolenceGraphicContent", 12 - "tools.ozone.report.defs#reasonViolenceGlorification", 13 - "tools.ozone.report.defs#reasonViolenceExtremistContent", 14 - "tools.ozone.report.defs#reasonViolenceTrafficking", 15 - "tools.ozone.report.defs#reasonViolenceOther", 16 - "tools.ozone.report.defs#reasonSexualAbuseContent", 17 - "tools.ozone.report.defs#reasonSexualNCII", 18 - "tools.ozone.report.defs#reasonSexualDeepfake", 19 - "tools.ozone.report.defs#reasonSexualAnimal", 20 - "tools.ozone.report.defs#reasonSexualUnlabeled", 21 - "tools.ozone.report.defs#reasonSexualOther", 22 - "tools.ozone.report.defs#reasonChildSafetyCSAM", 23 - "tools.ozone.report.defs#reasonChildSafetyGroom", 24 - "tools.ozone.report.defs#reasonChildSafetyPrivacy", 25 - "tools.ozone.report.defs#reasonChildSafetyHarassment", 26 - "tools.ozone.report.defs#reasonChildSafetyOther", 27 - "tools.ozone.report.defs#reasonHarassmentTroll", 28 - "tools.ozone.report.defs#reasonHarassmentTargeted", 29 - "tools.ozone.report.defs#reasonHarassmentHateSpeech", 30 - "tools.ozone.report.defs#reasonHarassmentDoxxing", 31 - "tools.ozone.report.defs#reasonHarassmentOther", 32 - "tools.ozone.report.defs#reasonMisleadingBot", 33 - "tools.ozone.report.defs#reasonMisleadingImpersonation", 34 - "tools.ozone.report.defs#reasonMisleadingSpam", 35 - "tools.ozone.report.defs#reasonMisleadingScam", 36 - "tools.ozone.report.defs#reasonMisleadingElections", 37 - "tools.ozone.report.defs#reasonMisleadingOther", 38 - "tools.ozone.report.defs#reasonRuleSiteSecurity", 39 - "tools.ozone.report.defs#reasonRuleProhibitedSales", 40 - "tools.ozone.report.defs#reasonRuleBanEvasion", 41 - "tools.ozone.report.defs#reasonRuleOther", 42 - "tools.ozone.report.defs#reasonSelfHarmContent", 43 - "tools.ozone.report.defs#reasonSelfHarmED", 44 - "tools.ozone.report.defs#reasonSelfHarmStunts", 45 - "tools.ozone.report.defs#reasonSelfHarmSubstances", 46 - "tools.ozone.report.defs#reasonSelfHarmOther" 47 - ] 48 - }, 49 - "reasonOther": { 50 - "type": "token", 51 - "description": "An issue not included in these options" 52 - }, 53 - "reasonAppeal": { 54 - "type": "token", 55 - "description": "Appeal a previously taken moderation action" 56 - }, 57 - "reasonRuleOther": { 58 - "type": "token", 59 - "description": "Other" 60 - }, 61 - "reasonSelfHarmED": { 62 - "type": "token", 63 - "description": "Eating disorders" 64 - }, 65 - "reasonSexualNCII": { 66 - "type": "token", 67 - "description": "Non-consensual intimate imagery" 68 - }, 69 - "reasonSexualOther": { 70 - "type": "token", 71 - "description": "Other sexual violence content" 72 - }, 73 - "reasonSexualAnimal": { 74 - "type": "token", 75 - "description": "Animal sexual abuse" 76 - }, 77 - "reasonMisleadingBot": { 78 - "type": "token", 79 - "description": "Fake account or bot" 80 - }, 81 - "reasonSelfHarmOther": { 82 - "type": "token", 83 - "description": "Other dangerous content" 84 - }, 85 - "reasonViolenceOther": { 86 - "type": "token", 87 - "description": "Other violent content" 88 - }, 89 - "reasonMisleadingScam": { 90 - "type": "token", 91 - "description": "Scam" 92 - }, 93 - "reasonMisleadingSpam": { 94 - "type": "token", 95 - "description": "Spam" 96 - }, 97 - "reasonRuleBanEvasion": { 98 - "type": "token", 99 - "description": "Banned user returning" 100 - }, 101 - "reasonSelfHarmStunts": { 102 - "type": "token", 103 - "description": "Dangerous challenges or activities" 104 - }, 105 - "reasonSexualDeepfake": { 106 - "type": "token", 107 - "description": "Deepfake adult content" 108 - }, 109 - "reasonViolenceAnimal": { 110 - "type": "token", 111 - "description": "Animal welfare violations" 112 - }, 113 - "reasonChildSafetyCSAM": { 114 - "type": "token", 115 - "description": "Child sexual abuse material (CSAM). These reports will be sent only be sent to the application's Moderation Authority." 116 - }, 117 - "reasonHarassmentOther": { 118 - "type": "token", 119 - "description": "Other harassing or hateful content" 120 - }, 121 - "reasonHarassmentTroll": { 122 - "type": "token", 123 - "description": "Trolling" 124 - }, 125 - "reasonMisleadingOther": { 126 - "type": "token", 127 - "description": "Other misleading content" 128 - }, 129 - "reasonSelfHarmContent": { 130 - "type": "token", 131 - "description": "Content promoting or depicting self-harm" 132 - }, 133 - "reasonSexualUnlabeled": { 134 - "type": "token", 135 - "description": "Unlabelled adult content" 136 - }, 137 - "reasonViolenceThreats": { 138 - "type": "token", 139 - "description": "Threats or incitement" 140 - }, 141 - "reasonChildSafetyGroom": { 142 - "type": "token", 143 - "description": "Grooming or predatory behavior. These reports will be sent only be sent to the application's Moderation Authority." 144 - }, 145 - "reasonChildSafetyOther": { 146 - "type": "token", 147 - "description": "Other child safety. These reports will be sent only be sent to the application's Moderation Authority." 148 - }, 149 - "reasonRuleSiteSecurity": { 150 - "type": "token", 151 - "description": "Hacking or system attacks" 152 - }, 153 - "reasonHarassmentDoxxing": { 154 - "type": "token", 155 - "description": "Doxxing" 156 - }, 157 - "reasonChildSafetyPrivacy": { 158 - "type": "token", 159 - "description": "Privacy violation involving a minor" 160 - }, 161 - "reasonHarassmentTargeted": { 162 - "type": "token", 163 - "description": "Targeted harassment" 164 - }, 165 - "reasonSelfHarmSubstances": { 166 - "type": "token", 167 - "description": "Dangerous substances or drug abuse" 168 - }, 169 - "reasonSexualAbuseContent": { 170 - "type": "token", 171 - "description": "Adult sexual abuse content" 172 - }, 173 - "reasonMisleadingElections": { 174 - "type": "token", 175 - "description": "False information about elections" 176 - }, 177 - "reasonRuleProhibitedSales": { 178 - "type": "token", 179 - "description": "Promoting or selling prohibited items or services" 180 - }, 181 - "reasonViolenceTrafficking": { 182 - "type": "token", 183 - "description": "Human trafficking" 184 - }, 185 - "reasonHarassmentHateSpeech": { 186 - "type": "token", 187 - "description": "Hate speech" 188 - }, 189 - "reasonChildSafetyHarassment": { 190 - "type": "token", 191 - "description": "Harassment or bullying of minors" 192 - }, 193 - "reasonViolenceGlorification": { 194 - "type": "token", 195 - "description": "Glorification of violence" 196 - }, 197 - "reasonViolenceGraphicContent": { 198 - "type": "token", 199 - "description": "Graphic violent content" 200 - }, 201 - "reasonMisleadingImpersonation": { 202 - "type": "token", 203 - "description": "Impersonation" 204 - }, 205 - "reasonViolenceExtremistContent": { 206 - "type": "token", 207 - "description": "Extremist content. These reports will be sent only be sent to the application's Moderation Authority." 208 - } 209 - }, 210 - "$type": "com.atproto.lexicon.schema", 211 - "lexicon": 1 212 - } 2 + "id": "tools.ozone.report.defs", 3 + "defs": { 4 + "reasonType": { 5 + "type": "string", 6 + "knownValues": [ 7 + "tools.ozone.report.defs#reasonAppeal", 8 + "tools.ozone.report.defs#reasonOther", 9 + "tools.ozone.report.defs#reasonViolenceAnimal", 10 + "tools.ozone.report.defs#reasonViolenceThreats", 11 + "tools.ozone.report.defs#reasonViolenceGraphicContent", 12 + "tools.ozone.report.defs#reasonViolenceGlorification", 13 + "tools.ozone.report.defs#reasonViolenceExtremistContent", 14 + "tools.ozone.report.defs#reasonViolenceTrafficking", 15 + "tools.ozone.report.defs#reasonViolenceOther", 16 + "tools.ozone.report.defs#reasonSexualAbuseContent", 17 + "tools.ozone.report.defs#reasonSexualNCII", 18 + "tools.ozone.report.defs#reasonSexualDeepfake", 19 + "tools.ozone.report.defs#reasonSexualAnimal", 20 + "tools.ozone.report.defs#reasonSexualUnlabeled", 21 + "tools.ozone.report.defs#reasonSexualOther", 22 + "tools.ozone.report.defs#reasonChildSafetyCSAM", 23 + "tools.ozone.report.defs#reasonChildSafetyGroom", 24 + "tools.ozone.report.defs#reasonChildSafetyPrivacy", 25 + "tools.ozone.report.defs#reasonChildSafetyHarassment", 26 + "tools.ozone.report.defs#reasonChildSafetyOther", 27 + "tools.ozone.report.defs#reasonHarassmentTroll", 28 + "tools.ozone.report.defs#reasonHarassmentTargeted", 29 + "tools.ozone.report.defs#reasonHarassmentHateSpeech", 30 + "tools.ozone.report.defs#reasonHarassmentDoxxing", 31 + "tools.ozone.report.defs#reasonHarassmentOther", 32 + "tools.ozone.report.defs#reasonMisleadingBot", 33 + "tools.ozone.report.defs#reasonMisleadingImpersonation", 34 + "tools.ozone.report.defs#reasonMisleadingSpam", 35 + "tools.ozone.report.defs#reasonMisleadingScam", 36 + "tools.ozone.report.defs#reasonMisleadingElections", 37 + "tools.ozone.report.defs#reasonMisleadingOther", 38 + "tools.ozone.report.defs#reasonRuleSiteSecurity", 39 + "tools.ozone.report.defs#reasonRuleProhibitedSales", 40 + "tools.ozone.report.defs#reasonRuleBanEvasion", 41 + "tools.ozone.report.defs#reasonRuleOther", 42 + "tools.ozone.report.defs#reasonSelfHarmContent", 43 + "tools.ozone.report.defs#reasonSelfHarmED", 44 + "tools.ozone.report.defs#reasonSelfHarmStunts", 45 + "tools.ozone.report.defs#reasonSelfHarmSubstances", 46 + "tools.ozone.report.defs#reasonSelfHarmOther" 47 + ] 48 + }, 49 + "reasonOther": { 50 + "type": "token", 51 + "description": "An issue not included in these options" 52 + }, 53 + "reasonAppeal": { 54 + "type": "token", 55 + "description": "Appeal a previously taken moderation action" 56 + }, 57 + "reasonRuleOther": { 58 + "type": "token", 59 + "description": "Other" 60 + }, 61 + "reasonSelfHarmED": { 62 + "type": "token", 63 + "description": "Eating disorders" 64 + }, 65 + "reasonSexualNCII": { 66 + "type": "token", 67 + "description": "Non-consensual intimate imagery" 68 + }, 69 + "reasonSexualOther": { 70 + "type": "token", 71 + "description": "Other sexual violence content" 72 + }, 73 + "reasonSexualAnimal": { 74 + "type": "token", 75 + "description": "Animal sexual abuse" 76 + }, 77 + "reasonMisleadingBot": { 78 + "type": "token", 79 + "description": "Fake account or bot" 80 + }, 81 + "reasonSelfHarmOther": { 82 + "type": "token", 83 + "description": "Other dangerous content" 84 + }, 85 + "reasonViolenceOther": { 86 + "type": "token", 87 + "description": "Other violent content" 88 + }, 89 + "reasonMisleadingScam": { 90 + "type": "token", 91 + "description": "Scam" 92 + }, 93 + "reasonMisleadingSpam": { 94 + "type": "token", 95 + "description": "Spam" 96 + }, 97 + "reasonRuleBanEvasion": { 98 + "type": "token", 99 + "description": "Banned user returning" 100 + }, 101 + "reasonSelfHarmStunts": { 102 + "type": "token", 103 + "description": "Dangerous challenges or activities" 104 + }, 105 + "reasonSexualDeepfake": { 106 + "type": "token", 107 + "description": "Deepfake adult content" 108 + }, 109 + "reasonViolenceAnimal": { 110 + "type": "token", 111 + "description": "Animal welfare violations" 112 + }, 113 + "reasonChildSafetyCSAM": { 114 + "type": "token", 115 + "description": "Child sexual abuse material (CSAM). These reports will be sent only be sent to the application's Moderation Authority." 116 + }, 117 + "reasonHarassmentOther": { 118 + "type": "token", 119 + "description": "Other harassing or hateful content" 120 + }, 121 + "reasonHarassmentTroll": { 122 + "type": "token", 123 + "description": "Trolling" 124 + }, 125 + "reasonMisleadingOther": { 126 + "type": "token", 127 + "description": "Other misleading content" 128 + }, 129 + "reasonSelfHarmContent": { 130 + "type": "token", 131 + "description": "Content promoting or depicting self-harm" 132 + }, 133 + "reasonSexualUnlabeled": { 134 + "type": "token", 135 + "description": "Unlabelled adult content" 136 + }, 137 + "reasonViolenceThreats": { 138 + "type": "token", 139 + "description": "Threats or incitement" 140 + }, 141 + "reasonChildSafetyGroom": { 142 + "type": "token", 143 + "description": "Grooming or predatory behavior. These reports will be sent only be sent to the application's Moderation Authority." 144 + }, 145 + "reasonChildSafetyOther": { 146 + "type": "token", 147 + "description": "Other child safety. These reports will be sent only be sent to the application's Moderation Authority." 148 + }, 149 + "reasonRuleSiteSecurity": { 150 + "type": "token", 151 + "description": "Hacking or system attacks" 152 + }, 153 + "reasonHarassmentDoxxing": { 154 + "type": "token", 155 + "description": "Doxxing" 156 + }, 157 + "reasonChildSafetyPrivacy": { 158 + "type": "token", 159 + "description": "Privacy violation involving a minor" 160 + }, 161 + "reasonHarassmentTargeted": { 162 + "type": "token", 163 + "description": "Targeted harassment" 164 + }, 165 + "reasonSelfHarmSubstances": { 166 + "type": "token", 167 + "description": "Dangerous substances or drug abuse" 168 + }, 169 + "reasonSexualAbuseContent": { 170 + "type": "token", 171 + "description": "Adult sexual abuse content" 172 + }, 173 + "reasonMisleadingElections": { 174 + "type": "token", 175 + "description": "False information about elections" 176 + }, 177 + "reasonRuleProhibitedSales": { 178 + "type": "token", 179 + "description": "Promoting or selling prohibited items or services" 180 + }, 181 + "reasonViolenceTrafficking": { 182 + "type": "token", 183 + "description": "Human trafficking" 184 + }, 185 + "reasonHarassmentHateSpeech": { 186 + "type": "token", 187 + "description": "Hate speech" 188 + }, 189 + "reasonChildSafetyHarassment": { 190 + "type": "token", 191 + "description": "Harassment or bullying of minors" 192 + }, 193 + "reasonViolenceGlorification": { 194 + "type": "token", 195 + "description": "Glorification of violence" 196 + }, 197 + "reasonViolenceGraphicContent": { 198 + "type": "token", 199 + "description": "Graphic violent content" 200 + }, 201 + "reasonMisleadingImpersonation": { 202 + "type": "token", 203 + "description": "Impersonation" 204 + }, 205 + "reasonViolenceExtremistContent": { 206 + "type": "token", 207 + "description": "Extremist content. These reports will be sent only be sent to the application's Moderation Authority." 208 + } 209 + }, 210 + "$type": "com.atproto.lexicon.schema", 211 + "lexicon": 1 212 + }
+7 -7
litefs.ingester.yml
··· 2 2 # This machine is the only writer for the content database 3 3 4 4 fuse: 5 - dir: "/litefs" 5 + dir: '/litefs' 6 6 7 7 data: 8 - dir: "/var/lib/litefs" 8 + dir: '/var/lib/litefs' 9 9 10 10 lease: 11 - type: "consul" 12 - advertise-url: "http://${HOSTNAME}.vm.${FLY_APP_NAME}.internal:20202" 11 + type: 'consul' 12 + advertise-url: 'http://${HOSTNAME}.vm.${FLY_APP_NAME}.internal:20202' 13 13 candidate: true 14 14 consul: 15 - url: "${FLY_CONSUL_URL}" 16 - key: "papili-one/primary" 15 + url: '${FLY_CONSUL_URL}' 16 + key: 'papili-one/primary' 17 17 18 18 exec: 19 - - cmd: "pnpm exec tsx src/ingester/main.ts" 19 + - cmd: 'pnpm exec tsx src/ingester/main.ts'
+7 -7
litefs.yml
··· 2 2 # This machine reads from the ingester primary 3 3 4 4 fuse: 5 - dir: "/litefs" 5 + dir: '/litefs' 6 6 7 7 data: 8 - dir: "/var/lib/litefs" 8 + dir: '/var/lib/litefs' 9 9 10 10 lease: 11 - type: "consul" 12 - advertise-url: "http://${HOSTNAME}.vm.${FLY_APP_NAME}.internal:20202" 11 + type: 'consul' 12 + advertise-url: 'http://${HOSTNAME}.vm.${FLY_APP_NAME}.internal:20202' 13 13 consul: 14 - url: "${FLY_CONSUL_URL}" 15 - key: "papili-one/primary" 14 + url: '${FLY_CONSUL_URL}' 15 + key: 'papili-one/primary' 16 16 17 17 exec: 18 - - cmd: "node build" 18 + - cmd: 'node build'
+3 -1
package.json
··· 5 5 "type": "module", 6 6 "pnpm": { 7 7 "peerDependencyRules": { 8 - "ignoreMissing": ["better-sqlite3"] 8 + "ignoreMissing": [ 9 + "better-sqlite3" 10 + ] 9 11 } 10 12 }, 11 13 "scripts": {
+9 -16
src/hooks.server.ts
··· 1 1 import { json, type Handle } from '@sveltejs/kit'; 2 2 import { getCurrentDid } from '$lib/server/auth'; 3 - import { 4 - checkRateLimit, 5 - getRateLimitKey, 6 - getRateLimitConfig 7 - } from '$lib/server/rate-limit'; 3 + import { checkRateLimit, getRateLimitKey, getRateLimitConfig } from '$lib/server/rate-limit'; 8 4 9 5 // Ingester runs as a separate process on the LiteFS primary machine 10 6 // See src/ingester/main.ts and fly.ingester.toml ··· 24 20 25 21 if (!allowed) { 26 22 const retryAfter = Math.ceil((resetAt - Date.now()) / 1000); 27 - return new Response( 28 - JSON.stringify({ error: 'Too many requests' }), 29 - { 30 - status: 429, 31 - headers: { 32 - 'Content-Type': 'application/json', 33 - 'Retry-After': String(retryAfter), 34 - 'X-RateLimit-Remaining': '0', 35 - 'X-RateLimit-Reset': String(resetAt) 36 - } 23 + return new Response(JSON.stringify({ error: 'Too many requests' }), { 24 + status: 429, 25 + headers: { 26 + 'Content-Type': 'application/json', 27 + 'Retry-After': String(retryAfter), 28 + 'X-RateLimit-Remaining': '0', 29 + 'X-RateLimit-Reset': String(resetAt) 37 30 } 38 - ); 31 + }); 39 32 } 40 33 41 34 // Add rate limit headers to response
+11 -11
src/ingester/handler.ts
··· 37 37 break; 38 38 } 39 39 } catch (err) { 40 - recordJetstreamEvent(event.kind, event.kind === 'commit' ? event.commit.collection : undefined, false); 40 + recordJetstreamEvent( 41 + event.kind, 42 + event.kind === 'commit' ? event.commit.collection : undefined, 43 + false 44 + ); 41 45 throw err; 42 46 } 43 47 } 44 48 45 - private async handleCommit( 46 - event: JetstreamEvent & { kind: 'commit' } 47 - ): Promise<void> { 49 + private async handleCommit(event: JetstreamEvent & { kind: 'commit' }): Promise<void> { 48 50 const { commit, did } = event; 49 51 const { collection, rkey, operation } = commit; 50 52 ··· 159 161 }); 160 162 } 161 163 162 - private async handleAccount( 163 - event: JetstreamEvent & { kind: 'account' } 164 - ): Promise<void> { 164 + private async handleAccount(event: JetstreamEvent & { kind: 'account' }): Promise<void> { 165 165 const { account, did } = event; 166 166 const now = new Date().toISOString(); 167 167 168 168 // Only update if this event is newer (higher seq) 169 - console.log(`[handler] Account event for ${did}: active=${account.active}, status=${account.status ?? 'none'}`); 169 + console.log( 170 + `[handler] Account event for ${did}: active=${account.active}, status=${account.status ?? 'none'}` 171 + ); 170 172 171 173 await this.db 172 174 .insert(accounts) ··· 189 191 }); 190 192 } 191 193 192 - private async handleIdentity( 193 - event: JetstreamEvent & { kind: 'identity' } 194 - ): Promise<void> { 194 + private async handleIdentity(event: JetstreamEvent & { kind: 'identity' }): Promise<void> { 195 195 const { identity, did } = event; 196 196 const now = new Date().toISOString(); 197 197
+2 -1
src/instrumentation.server.ts
··· 71 71 72 72 // Graceful shutdown 73 73 process.on('SIGTERM', () => { 74 - sdk.shutdown() 74 + sdk 75 + .shutdown() 75 76 .then(() => console.log('[otel] Telemetry shut down')) 76 77 .catch((err) => console.error('[otel] Error shutting down:', err)) 77 78 .finally(() => process.exit(0));
+19 -4
src/lib/components/Avatar.svelte
··· 19 19 lg: 'h-10 w-10 text-base' 20 20 }; 21 21 22 - const ringClasses = size === 'xs' ? '' : 'ring-2 ring-gray-200 ring-offset-1 ring-offset-white dark:ring-gray-600 dark:ring-offset-gray-950'; 22 + const ringClasses = 23 + size === 'xs' 24 + ? '' 25 + : 'ring-2 ring-gray-200 ring-offset-1 ring-offset-white dark:ring-gray-600 dark:ring-offset-gray-950'; 23 26 24 27 const profileUrl = did ? `/profile/${did}` : `/profile/${handle}`; 25 28 ··· 43 46 class="{sizeClasses[size]} {ringClasses} rounded-full object-cover" 44 47 /> 45 48 {:else} 46 - <div class="{sizeClasses[size]} {ringClasses} flex items-center justify-center rounded-full bg-gray-200 dark:bg-gray-700"> 49 + <div 50 + class="{sizeClasses[ 51 + size 52 + ]} {ringClasses} flex items-center justify-center rounded-full bg-gray-200 dark:bg-gray-700" 53 + > 47 54 <span class="font-medium text-gray-600 dark:text-gray-300"> 48 55 {handle.charAt(0).toUpperCase()} 49 56 </span> ··· 65 72 <img 66 73 src={avatar} 67 74 alt={handle} 68 - class="{sizeClasses[size]} {ringClasses} rounded-full object-cover {copied ? 'ring-green-500 dark:ring-green-400' : ''}" 75 + class="{sizeClasses[size]} {ringClasses} rounded-full object-cover {copied 76 + ? 'ring-green-500 dark:ring-green-400' 77 + : ''}" 69 78 /> 70 79 {:else} 71 - <div class="{sizeClasses[size]} {ringClasses} flex items-center justify-center rounded-full bg-gray-200 dark:bg-gray-700 {copied ? 'ring-green-500 dark:ring-green-400' : ''}"> 80 + <div 81 + class="{sizeClasses[ 82 + size 83 + ]} {ringClasses} flex items-center justify-center rounded-full bg-gray-200 dark:bg-gray-700 {copied 84 + ? 'ring-green-500 dark:ring-green-400' 85 + : ''}" 86 + > 72 87 <span class="font-medium text-gray-600 dark:text-gray-300"> 73 88 {handle.charAt(0).toUpperCase()} 74 89 </span>
+5 -1
src/lib/components/CommentSkeleton.svelte
··· 9 9 10 10 <div class="space-y-4"> 11 11 {#each Array(count) as _, i (i)} 12 - <div class="animate-pulse {nested ? 'ml-4 pl-3 border-l-2 border-gray-200 dark:border-gray-700' : ''}"> 12 + <div 13 + class="animate-pulse {nested 14 + ? 'ml-4 pl-3 border-l-2 border-gray-200 dark:border-gray-700' 15 + : ''}" 16 + > 13 17 <!-- Header --> 14 18 <div class="flex items-center gap-2 mb-2"> 15 19 <div class="w-5 h-5 bg-gray-200 dark:bg-gray-700 rounded-full"></div>
+9 -2
src/lib/components/Modal.svelte
··· 36 36 aria-labelledby={title ? 'modal-title' : undefined} 37 37 > 38 38 {#if title} 39 - <div class="flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700"> 39 + <div 40 + class="flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700" 41 + > 40 42 <h2 id="modal-title" class="text-lg font-semibold text-gray-900 dark:text-gray-100"> 41 43 {title} 42 44 </h2> ··· 47 49 aria-label="Close" 48 50 > 49 51 <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> 50 - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> 52 + <path 53 + stroke-linecap="round" 54 + stroke-linejoin="round" 55 + stroke-width="2" 56 + d="M6 18L18 6M6 6l12 12" 57 + /> 51 58 </svg> 52 59 </button> 53 60 </div>
+26 -6
src/lib/components/PostList.svelte
··· 28 28 currentUserHandle?: string; 29 29 } 30 30 31 - let { posts, emptyMessage = 'No posts yet.', canVote = false, currentUserDid, currentUserHandle }: Props = $props(); 31 + let { 32 + posts, 33 + emptyMessage = 'No posts yet.', 34 + canVote = false, 35 + currentUserDid, 36 + currentUserHandle 37 + }: Props = $props(); 32 38 33 39 // Get pending posts using $ auto-subscription, filtering out duplicates 34 40 let realRkeys = $derived(new Set(posts.map((p) => p.rkey))); ··· 55 61 <div class="py-12 text-center text-gray-500 dark:text-gray-400"> 56 62 <p>{emptyMessage}</p> 57 63 <p class="mt-2"> 58 - Be the first to <a href="/submit" class="text-violet-600 hover:underline dark:text-violet-400">submit a link</a>. 64 + Be the first to <a href="/submit" class="text-violet-600 hover:underline dark:text-violet-400" 65 + >submit a link</a 66 + >. 59 67 </p> 60 68 </div> 61 69 {:else} ··· 65 73 <li class="flex gap-2 text-sm opacity-70"> 66 74 <span class="w-6 text-right text-gray-400 dark:text-gray-500 select-none"> 67 75 <svg class="w-4 h-4 animate-spin inline" fill="none" viewBox="0 0 24 24"> 68 - <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> 69 - <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> 76 + <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" 77 + ></circle> 78 + <path 79 + class="opacity-75" 80 + fill="currentColor" 81 + d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" 82 + ></path> 70 83 </svg> 71 84 </span> 72 85 <div class="flex-1 min-w-0"> ··· 97 110 <span class="italic">posting...</span> 98 111 </div> 99 112 </div> 100 - <span class="flex-shrink-0 text-xs text-gray-400 dark:text-gray-500 self-start mt-0.5">0</span> 113 + <span class="flex-shrink-0 text-xs text-gray-400 dark:text-gray-500 self-start mt-0.5" 114 + >0</span 115 + > 101 116 </li> 102 117 {/each} 103 118 <!-- Real posts --> ··· 140 155 title="{post.commentCount ?? 0} comment{post.commentCount === 1 ? '' : 's'}" 141 156 > 142 157 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> 143 - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" /> 158 + <path 159 + stroke-linecap="round" 160 + stroke-linejoin="round" 161 + stroke-width="1.5" 162 + d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" 163 + /> 144 164 </svg> 145 165 <span>{post.commentCount ?? 0}</span> 146 166 </a>
+2 -1
src/lib/components/PostTitle.svelte
··· 13 13 14 14 let { title, rkey, url, titleSnippet, class: className = '' }: Props = $props(); 15 15 16 - const titleClass = `text-gray-900 dark:text-gray-100 visited:text-gray-500 dark:visited:text-gray-400 hover:underline ${className}`.trim(); 16 + const titleClass = 17 + `text-gray-900 dark:text-gray-100 visited:text-gray-500 dark:visited:text-gray-400 hover:underline ${className}`.trim(); 17 18 </script> 18 19 19 20 {#if url}
+1 -3
src/lib/components/ReportButton.svelte
··· 74 74 <p class="error">{error}</p> 75 75 {/if} 76 76 <div class="actions"> 77 - <button type="button" class="cancel-btn" onclick={cancel} disabled={loading}> 78 - Cancel 79 - </button> 77 + <button type="button" class="cancel-btn" onclick={cancel} disabled={loading}> Cancel </button> 80 78 <button type="button" class="submit-btn" onclick={submitReport} disabled={loading}> 81 79 {loading ? 'Sending...' : 'Submit Report'} 82 80 </button>
+2 -1
src/lib/components/Tabs.svelte
··· 24 24 ? 'border-violet-600 text-violet-600 dark:border-violet-400 dark:text-violet-400' 25 25 : 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'}" 26 26 > 27 - {tab.label}{#if tab.count !== undefined} ({tab.count}){/if} 27 + {tab.label}{#if tab.count !== undefined} 28 + ({tab.count}){/if} 28 29 </button> 29 30 {/each} 30 31 </nav>
+11 -1
src/lib/server/auth/session.ts
··· 18 18 if (value === undefined) return undefined; 19 19 return { name, value }; 20 20 }, 21 - set(name: string, value: string, options?: { path?: string; httpOnly?: boolean; secure?: boolean; sameSite?: 'lax' | 'strict' | 'none'; maxAge?: number }) { 21 + set( 22 + name: string, 23 + value: string, 24 + options?: { 25 + path?: string; 26 + httpOnly?: boolean; 27 + secure?: boolean; 28 + sameSite?: 'lax' | 'strict' | 'none'; 29 + maxAge?: number; 30 + } 31 + ) { 22 32 cookies.set(name, value, { 23 33 path: options?.path || '/', 24 34 httpOnly: options?.httpOnly ?? true,
+4 -7
src/lib/server/auth/storage.ts
··· 26 26 const state = JSON.stringify(val); 27 27 const now = new Date().toISOString(); 28 28 29 - await this.db 30 - .insert(authState) 31 - .values({ key, state, createdAt: now }) 32 - .onConflictDoUpdate({ 33 - target: authState.key, 34 - set: { state } 35 - }); 29 + await this.db.insert(authState).values({ key, state, createdAt: now }).onConflictDoUpdate({ 30 + target: authState.key, 31 + set: { state } 32 + }); 36 33 37 34 // Clean up old states (older than 15 minutes) 38 35 const fifteenMinutesAgo = new Date(Date.now() - 15 * 60 * 1000).toISOString();
+1 -3
src/lib/server/profiles.spec.ts
··· 30 30 handle: 'test.bsky.social', 31 31 avatar: 'https://example.com/avatar.jpg' 32 32 }); 33 - expect(mockFetch).toHaveBeenCalledWith( 34 - expect.stringContaining('did%3Aplc%3Atest123') 35 - ); 33 + expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('did%3Aplc%3Atest123')); 36 34 }); 37 35 38 36 it('should return null on HTTP error', async () => {
+2 -1
src/lib/server/profiles.ts
··· 59 59 did: string 60 60 ): AuthorProfile { 61 61 return ( 62 - profiles.get(did) ?? ({ 62 + profiles.get(did) ?? 63 + ({ 63 64 did, 64 65 handle: did.slice(0, 20) + '...' 65 66 } as AuthorProfile)
+7 -1
src/lib/server/queries/comments.ts
··· 37 37 export async function getCommentsWithPostContext( 38 38 options: GetCommentsOptions = {} 39 39 ): Promise<CommentWithPostContext[]> { 40 - const { limit = DEFAULT_COMMENTS_LIMIT, where, includeHidden = false, authorDid, postUri } = options; 40 + const { 41 + limit = DEFAULT_COMMENTS_LIMIT, 42 + where, 43 + includeHidden = false, 44 + authorDid, 45 + postUri 46 + } = options; 41 47 42 48 // Build where conditions 43 49 const conditions: SQL[] = [];
+8 -4
src/lib/server/queries/posts.ts
··· 29 29 * This is the base query used by all post listing pages. 30 30 * Hidden posts are excluded by default unless includeHidden is true. 31 31 */ 32 - export async function getPostsWithCommentCount( 33 - options: GetPostsOptions = {} 34 - ): Promise<PostBase[]> { 35 - const { limit = DEFAULT_PAGE_SIZE, offset = 0, where, orderBy = 'createdAt', includeHidden = false } = options; 32 + export async function getPostsWithCommentCount(options: GetPostsOptions = {}): Promise<PostBase[]> { 33 + const { 34 + limit = DEFAULT_PAGE_SIZE, 35 + offset = 0, 36 + where, 37 + orderBy = 'createdAt', 38 + includeHidden = false 39 + } = options; 36 40 37 41 // Build where clause - always filter hidden unless explicitly requested 38 42 let whereClause: SQL | undefined;
+1 -5
src/lib/server/rate-limit.ts
··· 61 61 * Create a rate limit key from request info. 62 62 * Uses IP address + optional path prefix. 63 63 */ 64 - export function getRateLimitKey( 65 - ip: string | null, 66 - path: string, 67 - userDid?: string | null 68 - ): string { 64 + export function getRateLimitKey(ip: string | null, path: string, userDid?: string | null): string { 69 65 // Prefer user DID for authenticated requests (more accurate) 70 66 const identifier = userDid || ip || 'unknown'; 71 67
+4 -1
src/lib/server/search/queries.ts
··· 104 104 * Returns comments matching the query with author profiles and snippets. 105 105 * Excludes hidden comments and comments on hidden posts. 106 106 */ 107 - export async function searchComments(ftsQuery: string, limit: number = 50): Promise<SearchComment[]> { 107 + export async function searchComments( 108 + ftsQuery: string, 109 + limit: number = 50 110 + ): Promise<SearchComment[]> { 108 111 const commentsResult = await contentClient.execute({ 109 112 sql: ` 110 113 SELECT
+1 -3
src/lib/server/search/sanitize.ts
··· 23 23 .replace(/[*\-:^(){}[\]=<>!@#$%&|\\]/g, '') // Remove special chars 24 24 .trim(); 25 25 26 - const terms = sanitized 27 - .split(/\s+/) 28 - .filter((term) => term.length > 0); 26 + const terms = sanitized.split(/\s+/).filter((term) => term.length > 0); 29 27 30 28 if (terms.length === 0) { 31 29 return '';
+27 -14
src/routes/+layout.svelte
··· 31 31 <Logo /> 32 32 <button 33 33 type="button" 34 - onclick={() => alphaModalOpen = true} 34 + onclick={() => (alphaModalOpen = true)} 35 35 class="px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide bg-amber-400 text-amber-900 rounded hover:bg-amber-300 transition-colors" 36 36 > 37 37 alpha ··· 54 54 <a href="/admin" class="text-violet-200 hover:text-white">admin</a> 55 55 {/if} 56 56 <span class="text-violet-300">|</span> 57 - <a href="/profile/{data.user.did}" class="text-violet-200 hover:text-white">{data.user.handle}</a> 57 + <a href="/profile/{data.user.did}" class="text-violet-200 hover:text-white" 58 + >{data.user.handle}</a 59 + > 58 60 <span class="text-violet-300">|</span> 59 61 <a href="/logout" class="text-violet-200 hover:text-white">logout</a> 60 62 {:else if data.did} ··· 72 74 <button 73 75 type="button" 74 76 class="flex items-center gap-1 text-violet-200 hover:text-white" 75 - onclick={() => menuOpen = !menuOpen} 77 + onclick={() => (menuOpen = !menuOpen)} 76 78 aria-expanded={menuOpen} 77 79 aria-haspopup="true" 78 80 > 79 81 {#if data.user?.avatar} 80 82 <img src={data.user.avatar} alt="" class="w-6 h-6 rounded-full" /> 81 83 {:else} 82 - <div class="w-6 h-6 rounded-full bg-violet-500 flex items-center justify-center text-xs font-medium text-white"> 84 + <div 85 + class="w-6 h-6 rounded-full bg-violet-500 flex items-center justify-center text-xs font-medium text-white" 86 + > 83 87 {(data.user?.handle ?? data.did ?? '?').charAt(0).toUpperCase()} 84 88 </div> 85 89 {/if} 86 90 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> 87 - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d={menuOpen ? 'M6 18L18 6M6 6l12 12' : 'M19 9l-7 7-7-7'} /> 91 + <path 92 + stroke-linecap="round" 93 + stroke-linejoin="round" 94 + stroke-width="2" 95 + d={menuOpen ? 'M6 18L18 6M6 6l12 12' : 'M19 9l-7 7-7-7'} 96 + /> 88 97 </svg> 89 98 </button> 90 99 91 100 <!-- Dropdown menu --> 92 101 {#if menuOpen} 93 - <div class="absolute right-0 top-full mt-2 w-48 rounded-md bg-white dark:bg-gray-800 shadow-lg ring-1 ring-black/5 dark:ring-white/10 z-50"> 102 + <div 103 + class="absolute right-0 top-full mt-2 w-48 rounded-md bg-white dark:bg-gray-800 shadow-lg ring-1 ring-black/5 dark:ring-white/10 z-50" 104 + > 94 105 <div class="py-1"> 95 106 <a 96 107 href="/search" ··· 147 158 </main> 148 159 149 160 <footer class="border-t border-gray-200 dark:border-gray-800 mt-8"> 150 - <div class="mx-auto max-w-4xl px-2 sm:px-4 py-4 text-center text-xs text-gray-500 dark:text-gray-400"> 151 - <a href="https://atproto.com" class="hover:underline" target="_blank" rel="noopener">Powered by ATProto</a> 161 + <div 162 + class="mx-auto max-w-4xl px-2 sm:px-4 py-4 text-center text-xs text-gray-500 dark:text-gray-400" 163 + > 164 + <a href="https://atproto.com" class="hover:underline" target="_blank" rel="noopener" 165 + >Powered by ATProto</a 166 + > 152 167 </div> 153 168 </footer> 154 169 </div> 155 170 156 - <Modal open={alphaModalOpen} onclose={() => alphaModalOpen = false} title="Alpha Release"> 171 + <Modal open={alphaModalOpen} onclose={() => (alphaModalOpen = false)} title="Alpha Release"> 157 172 <div class="space-y-4 text-sm text-gray-700 dark:text-gray-300"> 158 173 <p> 159 174 <strong class="text-gray-900 dark:text-gray-100">papilione is currently in alpha.</strong> 160 175 </p> 161 - <p> 162 - This means the application is under active development and you may encounter: 163 - </p> 176 + <p>This means the application is under active development and you may encounter:</p> 164 177 <ul class="list-disc list-inside space-y-1 ml-2"> 165 178 <li>Breaking changes to lexicons and data schemas</li> 166 179 <li>Features that may be added, changed, or removed</li> ··· 168 181 <li>Potential data migrations or resets</li> 169 182 </ul> 170 183 <p> 171 - Your posts and comments are stored on the ATProto network via your PDS, but local data 172 - like votes may be affected by changes during the alpha period. 184 + Your posts and comments are stored on the ATProto network via your PDS, but local data like 185 + votes may be affected by changes during the alpha period. 173 186 </p> 174 187 <p> 175 188 Found a bug or have feedback?
+26 -13
src/routes/admin/+page.server.ts
··· 6 6 7 7 export const load: PageServerLoad = async () => { 8 8 // Get counts for dashboard 9 - const [ 10 - totalPosts, 11 - hiddenPosts, 12 - totalComments, 13 - hiddenComments, 14 - pendingReports 15 - ] = await Promise.all([ 16 - contentDb.select({ count: sql<number>`count(*)` }).from(posts).then(r => r[0].count), 17 - contentDb.select({ count: sql<number>`count(*)` }).from(posts).where(eq(posts.isHidden, 1)).then(r => r[0].count), 18 - contentDb.select({ count: sql<number>`count(*)` }).from(comments).then(r => r[0].count), 19 - contentDb.select({ count: sql<number>`count(*)` }).from(comments).where(eq(comments.isHidden, 1)).then(r => r[0].count), 20 - localDb.select({ count: sql<number>`count(*)` }).from(reports).where(isNull(reports.resolvedAt)).then(r => r[0].count) 21 - ]); 9 + const [totalPosts, hiddenPosts, totalComments, hiddenComments, pendingReports] = 10 + await Promise.all([ 11 + contentDb 12 + .select({ count: sql<number>`count(*)` }) 13 + .from(posts) 14 + .then((r) => r[0].count), 15 + contentDb 16 + .select({ count: sql<number>`count(*)` }) 17 + .from(posts) 18 + .where(eq(posts.isHidden, 1)) 19 + .then((r) => r[0].count), 20 + contentDb 21 + .select({ count: sql<number>`count(*)` }) 22 + .from(comments) 23 + .then((r) => r[0].count), 24 + contentDb 25 + .select({ count: sql<number>`count(*)` }) 26 + .from(comments) 27 + .where(eq(comments.isHidden, 1)) 28 + .then((r) => r[0].count), 29 + localDb 30 + .select({ count: sql<number>`count(*)` }) 31 + .from(reports) 32 + .where(isNull(reports.resolvedAt)) 33 + .then((r) => r[0].count) 34 + ]); 22 35 23 36 // Get recent reports 24 37 const recentReports = await localDb
+52 -17
src/routes/admin/+page.svelte
··· 11 11 12 12 <!-- Stats grid --> 13 13 <div class="grid grid-cols-2 md:grid-cols-5 gap-4"> 14 - <div class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-200 dark:border-gray-700"> 14 + <div 15 + class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-200 dark:border-gray-700" 16 + > 15 17 <div class="text-2xl font-bold text-gray-900 dark:text-gray-100">{data.stats.totalPosts}</div> 16 18 <div class="text-sm text-gray-500 dark:text-gray-400">Total Posts</div> 17 19 </div> 18 - <div class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-200 dark:border-gray-700"> 20 + <div 21 + class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-200 dark:border-gray-700" 22 + > 19 23 <div class="text-2xl font-bold text-red-600 dark:text-red-400">{data.stats.hiddenPosts}</div> 20 24 <div class="text-sm text-gray-500 dark:text-gray-400">Hidden Posts</div> 21 25 </div> 22 - <div class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-200 dark:border-gray-700"> 23 - <div class="text-2xl font-bold text-gray-900 dark:text-gray-100">{data.stats.totalComments}</div> 26 + <div 27 + class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-200 dark:border-gray-700" 28 + > 29 + <div class="text-2xl font-bold text-gray-900 dark:text-gray-100"> 30 + {data.stats.totalComments} 31 + </div> 24 32 <div class="text-sm text-gray-500 dark:text-gray-400">Total Comments</div> 25 33 </div> 26 - <div class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-200 dark:border-gray-700"> 27 - <div class="text-2xl font-bold text-red-600 dark:text-red-400">{data.stats.hiddenComments}</div> 34 + <div 35 + class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-200 dark:border-gray-700" 36 + > 37 + <div class="text-2xl font-bold text-red-600 dark:text-red-400"> 38 + {data.stats.hiddenComments} 39 + </div> 28 40 <div class="text-sm text-gray-500 dark:text-gray-400">Hidden Comments</div> 29 41 </div> 30 - <div class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-200 dark:border-gray-700"> 31 - <div class="text-2xl font-bold text-orange-600 dark:text-orange-400">{data.stats.pendingReports}</div> 42 + <div 43 + class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-200 dark:border-gray-700" 44 + > 45 + <div class="text-2xl font-bold text-orange-600 dark:text-orange-400"> 46 + {data.stats.pendingReports} 47 + </div> 32 48 <div class="text-sm text-gray-500 dark:text-gray-400">Pending Reports</div> 33 49 </div> 34 50 </div> ··· 37 53 {#if data.recentReports.length > 0} 38 54 <section> 39 55 <h2 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Pending Reports</h2> 40 - <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700"> 56 + <div 57 + class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700" 58 + > 41 59 {#each data.recentReports as report} 42 60 <div class="p-4"> 43 61 <div class="flex justify-between items-start gap-4"> 44 62 <div class="flex-1 min-w-0"> 45 63 <div class="text-sm font-medium text-gray-900 dark:text-gray-100"> 46 - {report.targetType}: <code class="text-xs bg-gray-100 dark:bg-gray-700 px-1 rounded">{report.targetUri}</code> 64 + {report.targetType}: 65 + <code class="text-xs bg-gray-100 dark:bg-gray-700 px-1 rounded" 66 + >{report.targetUri}</code 67 + > 47 68 </div> 48 69 {#if report.reason} 49 70 <p class="text-sm text-gray-600 dark:text-gray-400 mt-1">{report.reason}</p> 50 71 {/if} 51 72 <div class="text-xs text-gray-500 dark:text-gray-400 mt-1"> 52 - Reported by {report.reporterDid.slice(0, 20)}... on {new Date(report.createdAt).toLocaleDateString()} 73 + Reported by {report.reporterDid.slice(0, 20)}... on {new Date( 74 + report.createdAt 75 + ).toLocaleDateString()} 53 76 </div> 54 77 </div> 55 78 <div class="flex gap-2"> ··· 92 115 {#if data.recentlyHiddenPosts.length === 0} 93 116 <p class="text-sm text-gray-500 dark:text-gray-400">No hidden posts.</p> 94 117 {:else} 95 - <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700"> 118 + <div 119 + class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700" 120 + > 96 121 {#each data.recentlyHiddenPosts as post} 97 122 <div class="p-3 flex justify-between items-center gap-2"> 98 123 <div class="flex-1 min-w-0"> 99 - <div class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">{post.title}</div> 100 - <div class="text-xs text-gray-500 dark:text-gray-400 truncate">{post.authorDid}</div> 124 + <div class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate"> 125 + {post.title} 126 + </div> 127 + <div class="text-xs text-gray-500 dark:text-gray-400 truncate"> 128 + {post.authorDid} 129 + </div> 101 130 </div> 102 131 <form method="POST" action="/api/admin/moderate"> 103 132 <input type="hidden" name="uri" value={post.uri} /> ··· 122 151 {#if data.recentlyHiddenComments.length === 0} 123 152 <p class="text-sm text-gray-500 dark:text-gray-400">No hidden comments.</p> 124 153 {:else} 125 - <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700"> 154 + <div 155 + class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700" 156 + > 126 157 {#each data.recentlyHiddenComments as comment} 127 158 <div class="p-3 flex justify-between items-center gap-2"> 128 159 <div class="flex-1 min-w-0"> 129 - <div class="text-sm text-gray-900 dark:text-gray-100 truncate">{comment.text.slice(0, 50)}...</div> 130 - <div class="text-xs text-gray-500 dark:text-gray-400 truncate">{comment.authorDid}</div> 160 + <div class="text-sm text-gray-900 dark:text-gray-100 truncate"> 161 + {comment.text.slice(0, 50)}... 162 + </div> 163 + <div class="text-xs text-gray-500 dark:text-gray-400 truncate"> 164 + {comment.authorDid} 165 + </div> 131 166 </div> 132 167 <form method="POST" action="/api/admin/moderate"> 133 168 <input type="hidden" name="uri" value={comment.uri} />
+5 -2
src/routes/api/vote/+server.ts
··· 6 6 import { getCurrentDid } from '$lib/server/auth/session'; 7 7 8 8 // AT URI format: at://did:method:id/collection/rkey 9 - const AT_URI_PATTERN = /^at:\/\/did:[a-z]+:[a-zA-Z0-9._-]+\/[a-z]+\.[a-z]+\.[a-z]+\/[a-zA-Z0-9._-]+$/; 9 + const AT_URI_PATTERN = 10 + /^at:\/\/did:[a-z]+:[a-zA-Z0-9._-]+\/[a-z]+\.[a-z]+\.[a-z]+\/[a-zA-Z0-9._-]+$/; 10 11 const MAX_URI_LENGTH = 256; 11 12 12 13 export const POST: RequestHandler = async ({ request, cookies }) => { ··· 65 66 // Update or insert vote in local DB 66 67 if (value === 0) { 67 68 // Remove vote 68 - await localDb.delete(votes).where(and(eq(votes.userDid, userDid), eq(votes.targetUri, targetUri))); 69 + await localDb 70 + .delete(votes) 71 + .where(and(eq(votes.userDid, userDid), eq(votes.targetUri, targetUri))); 69 72 } else if (existingVote) { 70 73 // Update existing vote 71 74 await localDb
+2 -1
src/routes/api/vote/vote.spec.ts
··· 1 1 import { describe, expect, it } from 'vitest'; 2 2 3 3 // Constants matching the actual implementation 4 - const AT_URI_PATTERN = /^at:\/\/did:[a-z]+:[a-zA-Z0-9._-]+\/[a-z]+\.[a-z]+\.[a-z]+\/[a-zA-Z0-9._-]+$/; 4 + const AT_URI_PATTERN = 5 + /^at:\/\/did:[a-z]+:[a-zA-Z0-9._-]+\/[a-z]+\.[a-z]+\.[a-z]+\/[a-zA-Z0-9._-]+$/; 5 6 const MAX_URI_LENGTH = 256; 6 7 7 8 // Test vote API input validation logic - mirrors actual implementation
+7 -5
src/routes/login/+page.svelte
··· 13 13 14 14 <div class="max-w-sm mx-auto py-12"> 15 15 <h1 class="text-lg font-bold mb-2">Sign in</h1> 16 - <p class="text-sm text-gray-600 dark:text-gray-400 mb-6"> 17 - Sign in with your ATProto handle 18 - </p> 16 + <p class="text-sm text-gray-600 dark:text-gray-400 mb-6">Sign in with your ATProto handle</p> 19 17 20 18 {#if form?.error} 21 - <div class="mb-4 p-3 text-sm bg-red-50 text-red-700 dark:bg-red-900/20 dark:text-red-400 rounded"> 19 + <div 20 + class="mb-4 p-3 text-sm bg-red-50 text-red-700 dark:bg-red-900/20 dark:text-red-400 rounded" 21 + > 22 22 {form.error} 23 23 </div> 24 24 {/if} 25 25 26 26 {#if data.error} 27 - <div class="mb-4 p-3 text-sm bg-red-50 text-red-700 dark:bg-red-900/20 dark:text-red-400 rounded"> 27 + <div 28 + class="mb-4 p-3 text-sm bg-red-50 text-red-700 dark:bg-red-900/20 dark:text-red-400 rounded" 29 + > 28 30 {data.error} 29 31 </div> 30 32 {/if}
+15 -5
src/routes/login/login.spec.ts
··· 102 102 }); 103 103 104 104 it('should normalize handle without domain', async () => { 105 - const mockAuthorize = vi.fn().mockResolvedValue(new URL('https://auth.bsky.social/authorize')); 105 + const mockAuthorize = vi 106 + .fn() 107 + .mockResolvedValue(new URL('https://auth.bsky.social/authorize')); 106 108 mockCreateOAuthClient.mockResolvedValue({ authorize: mockAuthorize } as any); 107 109 108 110 const formData = new FormData(); ··· 119 121 }); 120 122 121 123 it('should preserve handle with domain', async () => { 122 - const mockAuthorize = vi.fn().mockResolvedValue(new URL('https://auth.bsky.social/authorize')); 124 + const mockAuthorize = vi 125 + .fn() 126 + .mockResolvedValue(new URL('https://auth.bsky.social/authorize')); 123 127 mockCreateOAuthClient.mockResolvedValue({ authorize: mockAuthorize } as any); 124 128 125 129 const formData = new FormData(); ··· 136 140 }); 137 141 138 142 it('should pass returnUrl in state', async () => { 139 - const mockAuthorize = vi.fn().mockResolvedValue(new URL('https://auth.bsky.social/authorize')); 143 + const mockAuthorize = vi 144 + .fn() 145 + .mockResolvedValue(new URL('https://auth.bsky.social/authorize')); 140 146 mockCreateOAuthClient.mockResolvedValue({ authorize: mockAuthorize } as any); 141 147 142 148 const formData = new FormData(); ··· 158 164 }); 159 165 160 166 it('should default returnUrl to /', async () => { 161 - const mockAuthorize = vi.fn().mockResolvedValue(new URL('https://auth.bsky.social/authorize')); 167 + const mockAuthorize = vi 168 + .fn() 169 + .mockResolvedValue(new URL('https://auth.bsky.social/authorize')); 162 170 mockCreateOAuthClient.mockResolvedValue({ authorize: mockAuthorize } as any); 163 171 164 172 const formData = new FormData(); ··· 197 205 }); 198 206 199 207 it('should handle OAuth authorize errors', async () => { 200 - const mockAuthorize = vi.fn().mockRejectedValue(new Error('Authorization server unavailable')); 208 + const mockAuthorize = vi 209 + .fn() 210 + .mockRejectedValue(new Error('Authorization server unavailable')); 201 211 mockCreateOAuthClient.mockResolvedValue({ authorize: mockAuthorize } as any); 202 212 203 213 const formData = new FormData();
+5 -1
src/routes/oauth/callback/+server.ts
··· 23 23 if (state) { 24 24 try { 25 25 const parsed = JSON.parse(state); 26 - if (parsed.returnUrl && typeof parsed.returnUrl === 'string' && parsed.returnUrl.startsWith('/')) { 26 + if ( 27 + parsed.returnUrl && 28 + typeof parsed.returnUrl === 'string' && 29 + parsed.returnUrl.startsWith('/') 30 + ) { 27 31 returnUrl = parsed.returnUrl; 28 32 } 29 33 } catch {
+3 -1
src/routes/oauth/callback/callback.spec.ts
··· 24 24 25 25 describe('error handling', () => { 26 26 it('should redirect to login with error from OAuth provider', async () => { 27 - const url = new URL('http://localhost/oauth/callback?error=access_denied&error_description=User%20denied%20access'); 27 + const url = new URL( 28 + 'http://localhost/oauth/callback?error=access_denied&error_description=User%20denied%20access' 29 + ); 28 30 29 31 await expect( 30 32 GET({
+2 -10
src/routes/post/[rkey]/+page.server.ts
··· 17 17 const userIsAdmin = isAdmin(locals.did); 18 18 19 19 // Find post by rkey (from content DB - LiteFS replica) 20 - const [post] = await contentDb 21 - .select() 22 - .from(posts) 23 - .where(eq(posts.rkey, rkey)) 24 - .limit(1); 20 + const [post] = await contentDb.select().from(posts).where(eq(posts.rkey, rkey)).limit(1); 25 21 26 22 if (!post) { 27 23 error(404, 'Post not found'); ··· 94 90 } 95 91 96 92 // Get the post from content DB 97 - const [post] = await contentDb 98 - .select() 99 - .from(posts) 100 - .where(eq(posts.rkey, rkey)) 101 - .limit(1); 93 + const [post] = await contentDb.select().from(posts).where(eq(posts.rkey, rkey)).limit(1); 102 94 103 95 if (!post) { 104 96 return fail(404, { error: 'Post not found' });
+35 -16
src/routes/post/[rkey]/+page.svelte
··· 86 86 } 87 87 88 88 // Build threaded comment structure, merging in pending comments 89 - function buildCommentTree(comments: Comment[], pendingComments: PendingComment[]): SvelteMap<string | null, Comment[]> { 89 + function buildCommentTree( 90 + comments: Comment[], 91 + pendingComments: PendingComment[] 92 + ): SvelteMap<string | null, Comment[]> { 90 93 const tree = new SvelteMap<string | null, Comment[]>(); 91 94 92 95 // Track real comment rkeys to avoid duplicates ··· 194 197 {/if} 195 198 {#if data.isAdmin} 196 199 · 197 - <AdminControls targetUri={data.post.uri} targetType="post" isHidden={!!data.post.isHidden} /> 200 + <AdminControls 201 + targetUri={data.post.uri} 202 + targetType="post" 203 + isHidden={!!data.post.isHidden} 204 + /> 198 205 {/if} 199 206 </p> 200 207 </div> ··· 210 217 211 218 <section class="space-y-4"> 212 219 <h2 class="text-sm font-medium text-gray-700 dark:text-gray-300"> 213 - {totalComments === 0 ? 'No comments yet' : `${totalComments} comment${totalComments === 1 ? '' : 's'}`} 220 + {totalComments === 0 221 + ? 'No comments yet' 222 + : `${totalComments} comment${totalComments === 1 ? '' : 's'}`} 214 223 </h2> 215 224 216 225 <!-- Comment form --> 217 226 {#if data.user} 218 227 {#if form?.error} 219 - <div class="p-3 text-sm bg-red-50 text-red-700 dark:bg-red-900/20 dark:text-red-400 rounded"> 228 + <div 229 + class="p-3 text-sm bg-red-50 text-red-700 dark:bg-red-900/20 dark:text-red-400 rounded" 230 + > 220 231 {form.error} 221 232 </div> 222 233 {/if} ··· 229 240 return async ({ update, result }) => { 230 241 if (result.type === 'success' && result.data?.success && result.data?.comment) { 231 242 // Add to pending store for optimistic UI 232 - pendingComments.add(result.data.comment as Omit<PendingComment, 'submittedAt'>); 243 + pendingComments.add(result.data.comment as Omit); 233 244 commentText = ''; 234 245 } else { 235 246 await update(); ··· 316 327 /> 317 328 {#if comment.isPending} 318 329 <svg class="w-3 h-3 animate-spin text-gray-400" fill="none" viewBox="0 0 24 24"> 319 - <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> 320 - <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> 330 + <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" 331 + ></circle> 332 + <path 333 + class="opacity-75" 334 + fill="currentColor" 335 + d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" 336 + ></path> 321 337 </svg> 322 338 <span class="comment-time italic">posting...</span> 323 339 {:else} ··· 334 350 /> 335 351 </div> 336 352 {:else if !isCollapsed} 337 - <span class="comment-points">{comment.voteCount} pt{comment.voteCount === 1 ? '' : 's'}</span> 353 + <span class="comment-points" 354 + >{comment.voteCount} pt{comment.voteCount === 1 ? '' : 's'}</span 355 + > 338 356 {/if} 339 357 {#if data.user && !isCollapsed} 340 - <button 341 - type="button" 342 - onclick={() => startReply(comment)} 343 - class="comment-action" 344 - > 358 + <button type="button" onclick={() => startReply(comment)} class="comment-action"> 345 359 reply 346 360 </button> 347 361 <ReportButton targetUri={comment.uri} targetType="comment" /> 348 362 {/if} 349 363 {#if data.isAdmin && !isCollapsed} 350 - <AdminControls targetUri={comment.uri} targetType="comment" isHidden={!!comment.isHidden} /> 364 + <AdminControls 365 + targetUri={comment.uri} 366 + targetType="comment" 367 + isHidden={!!comment.isHidden} 368 + /> 351 369 {/if} 352 370 {/if} 353 371 </div> ··· 355 373 <!-- Collapsed indicator --> 356 374 {#if isCollapsed} 357 375 <div class="collapsed-info"> 358 - {descendantCount} {descendantCount === 1 ? 'reply' : 'replies'} hidden 376 + {descendantCount} 377 + {descendantCount === 1 ? 'reply' : 'replies'} hidden 359 378 </div> 360 379 {:else} 361 380 <!-- Comment body --> ··· 373 392 return async ({ update, result }) => { 374 393 if (result.type === 'success' && result.data?.success && result.data?.comment) { 375 394 // Add to pending store for optimistic UI 376 - pendingComments.add(result.data.comment as Omit<PendingComment, 'submittedAt'>); 395 + pendingComments.add(result.data.comment as Omit); 377 396 cancelReply(); 378 397 } else { 379 398 await update();
+18 -11
src/routes/profile/[identifier]/+page.svelte
··· 27 27 <h1 class="text-xl font-semibold text-gray-900 dark:text-gray-100"> 28 28 @{data.profile.handle} 29 29 </h1> 30 - <p class="text-xs text-gray-500 dark:text-gray-400 font-mono truncate mt-1" title={data.profile.did}> 30 + <p 31 + class="text-xs text-gray-500 dark:text-gray-400 font-mono truncate mt-1" 32 + title={data.profile.did} 33 + > 31 34 {data.profile.did} 32 35 </p> 33 36 <div class="flex gap-4 mt-2 text-sm text-gray-600 dark:text-gray-400"> ··· 43 46 { id: 'posts', label: 'Posts' }, 44 47 { id: 'comments', label: 'Comments' } 45 48 ]} 46 - activeTab={activeTab} 49 + {activeTab} 47 50 onchange={(id) => (activeTab = id as 'posts' | 'comments')} 48 51 /> 49 52 50 53 <!-- Posts tab --> 51 54 {#if activeTab === 'posts'} 52 55 {#if data.posts.length === 0} 53 - <p class="text-sm text-gray-500 dark:text-gray-400 py-8 text-center"> 54 - No posts yet. 55 - </p> 56 + <p class="text-sm text-gray-500 dark:text-gray-400 py-8 text-center">No posts yet.</p> 56 57 {:else} 57 58 <ol class="space-y-2"> 58 59 {#each data.posts as post (post.uri)} ··· 74 75 <PostTitle title={post.title} rkey={post.rkey} url={post.url} /> 75 76 </div> 76 77 {#if post.text} 77 - <p class="text-xs text-gray-600 dark:text-gray-400 mt-0.5 line-clamp-2">{post.text}</p> 78 + <p class="text-xs text-gray-600 dark:text-gray-400 mt-0.5 line-clamp-2"> 79 + {post.text} 80 + </p> 78 81 {/if} 79 82 <div class="text-xs text-gray-500 dark:text-gray-400"> 80 83 {formatTimeAgo(post.createdAt)} 81 - · <a href="/post/{post.rkey}" class="hover:underline">{post.commentCount} comment{post.commentCount === 1 ? '' : 's'}</a> 84 + · 85 + <a href="/post/{post.rkey}" class="hover:underline" 86 + >{post.commentCount} comment{post.commentCount === 1 ? '' : 's'}</a 87 + > 82 88 </div> 83 89 </div> 84 90 </li> ··· 90 96 <!-- Comments tab --> 91 97 {#if activeTab === 'comments'} 92 98 {#if data.comments.length === 0} 93 - <p class="text-sm text-gray-500 dark:text-gray-400 py-8 text-center"> 94 - No comments yet. 95 - </p> 99 + <p class="text-sm text-gray-500 dark:text-gray-400 py-8 text-center">No comments yet.</p> 96 100 {:else} 97 101 <div class="space-y-4"> 98 102 {#each data.comments as comment (comment.uri)} 99 103 <div class="text-sm border-l-2 border-gray-200 dark:border-gray-700 pl-3"> 100 104 <div class="text-xs text-gray-500 dark:text-gray-400 mb-1"> 101 - on <a href="/post/{comment.postRkey}" class="text-violet-600 dark:text-violet-400 hover:underline"> 105 + on <a 106 + href="/post/{comment.postRkey}" 107 + class="text-violet-600 dark:text-violet-400 hover:underline" 108 + > 102 109 {comment.postTitle || 'a post'} 103 110 </a> 104 111 · {formatTimeAgo(comment.createdAt)}
+3 -1
src/routes/search/+page.svelte
··· 88 88 {@html post.textSnippet} 89 89 </p> 90 90 {/if} 91 - <div class="text-xs text-gray-500 dark:text-gray-400 flex items-center gap-1 mt-0.5"> 91 + <div 92 + class="text-xs text-gray-500 dark:text-gray-400 flex items-center gap-1 mt-0.5" 93 + > 92 94 <span>{post.voteCount} points</span> 93 95 <span>by</span> 94 96 <Avatar
+6 -1
src/routes/submit/+page.server.ts
··· 57 57 return fail(400, { error: 'Title must be 300 characters or less', url, title, text }); 58 58 } 59 59 if (text && text.length > 10000) { 60 - return fail(400, { error: 'Description must be 10,000 characters or less', url, title, text }); 60 + return fail(400, { 61 + error: 'Description must be 10,000 characters or less', 62 + url, 63 + title, 64 + text 65 + }); 61 66 } 62 67 63 68 const now = new Date().toISOString();
+11 -7
src/routes/submit/+page.svelte
··· 15 15 <h1 class="text-lg font-bold mb-4">Submit</h1> 16 16 17 17 {#if form?.error} 18 - <div class="mb-4 p-3 text-sm bg-red-50 text-red-700 dark:bg-red-900/20 dark:text-red-400 rounded"> 18 + <div 19 + class="mb-4 p-3 text-sm bg-red-50 text-red-700 dark:bg-red-900/20 dark:text-red-400 rounded" 20 + > 19 21 {form.error} 20 22 </div> 21 23 {/if} ··· 27 29 return async ({ result, update }) => { 28 30 if (result.type === 'success' && result.data?.success && result.data?.post) { 29 31 // Add to pending store for optimistic UI 30 - pendingPosts.add(result.data.post as Omit<PendingPost, 'submittedAt'>); 32 + pendingPosts.add(result.data.post as Omit); 31 33 // Navigate to home 32 34 await goto('/'); 33 35 } else { ··· 40 42 class="space-y-4" 41 43 > 42 44 <div> 43 - <label for="title" class="block text-sm font-medium mb-1"> 44 - Title 45 - </label> 45 + <label for="title" class="block text-sm font-medium mb-1"> Title </label> 46 46 <input 47 47 type="text" 48 48 id="title" ··· 82 82 maxlength="10000" 83 83 placeholder="Add context or commentary..." 84 84 class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-transparent resize-y" 85 - >{form?.text ?? ''}</textarea> 85 + >{form?.text ?? ''}</textarea 86 + > 86 87 </div> 87 88 88 89 <div class="flex items-center gap-4 pt-2"> ··· 97 98 Submit 98 99 {/if} 99 100 </button> 100 - <a href="/" class="text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-200"> 101 + <a 102 + href="/" 103 + class="text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-200" 104 + > 101 105 cancel 102 106 </a> 103 107 </div>