A community based topic aggregation platform built on atproto
11
fork

Configure Feed

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

Merge branch 'refactor/lexicon-atproto-best-practices-comprehensive'

+42 -169
+2
internal/atproto/lexicon/social/coves/community/defs.json
··· 20 20 "name": { 21 21 "type": "string", 22 22 "maxLength": 64, 23 + "maxGraphemes": 64, 23 24 "description": "Short community name" 24 25 }, 25 26 "displayName": { ··· 87 88 "name": { 88 89 "type": "string", 89 90 "maxLength": 64, 91 + "maxGraphemes": 64, 90 92 "description": "Short community name" 91 93 }, 92 94 "displayName": {
+2 -2
internal/atproto/lexicon/social/coves/community/post/get.json
··· 189 189 }, 190 190 "blockedBy": { 191 191 "type": "string", 192 - "enum": ["author", "community", "moderator"], 192 + "knownValues": ["author", "community", "moderator"], 193 193 "description": "What caused the block: viewer blocked author, viewer blocked community, or post was removed by moderators" 194 194 }, 195 195 "author": { ··· 266 266 "properties": { 267 267 "vote": { 268 268 "type": "string", 269 - "enum": ["up", "down"], 269 + "knownValues": ["up", "down"], 270 270 "description": "Viewer's vote on this post" 271 271 }, 272 272 "voteUri": {
+3 -3
internal/atproto/lexicon/social/coves/community/post/search.json
··· 25 25 }, 26 26 "type": { 27 27 "type": "string", 28 - "enum": ["text", "image", "video", "article", "microblog"], 28 + "knownValues": ["text", "image", "video", "article", "microblog"], 29 29 "description": "Filter by post type" 30 30 }, 31 31 "tags": { ··· 37 37 }, 38 38 "sort": { 39 39 "type": "string", 40 - "enum": ["relevance", "new", "top"], 40 + "knownValues": ["relevance", "new", "top"], 41 41 "default": "relevance" 42 42 }, 43 43 "timeframe": { 44 44 "type": "string", 45 - "enum": ["hour", "day", "week", "month", "year", "all"], 45 + "knownValues": ["hour", "day", "week", "month", "year", "all"], 46 46 "default": "all" 47 47 }, 48 48 "limit": {
+2
internal/atproto/lexicon/social/coves/community/post/update.json
··· 25 25 "content": { 26 26 "type": "string", 27 27 "maxLength": 50000, 28 + "maxGraphemes": 20000, 28 29 "description": "Updated content - main text for text posts, description for media, etc." 29 30 }, 30 31 "facets": { ··· 72 73 "editNote": { 73 74 "type": "string", 74 75 "maxLength": 300, 76 + "maxGraphemes": 300, 75 77 "description": "Optional note explaining the edit" 76 78 } 77 79 }
+1
internal/atproto/lexicon/social/coves/community/profile.json
··· 13 13 "name": { 14 14 "type": "string", 15 15 "maxLength": 64, 16 + "maxGraphemes": 64, 16 17 "description": "Short community name (local part of handle)" 17 18 }, 18 19 "displayName": {
+1 -1
internal/atproto/lexicon/social/coves/community/search.json
··· 43 43 "type": "array", 44 44 "items": { 45 45 "type": "ref", 46 - "ref": "social.coves.community.list#communityView" 46 + "ref": "social.coves.community.defs#communityView" 47 47 } 48 48 }, 49 49 "cursor": {
+3 -1
internal/atproto/lexicon/social/coves/embed/external.json
··· 15 15 "title": { 16 16 "type": "string", 17 17 "maxLength": 500, 18 + "maxGraphemes": 500, 18 19 "description": "Title of the linked content" 19 20 }, 20 21 "description": { 21 22 "type": "string", 22 23 "maxLength": 1000, 24 + "maxGraphemes": 1000, 23 25 "description": "Description or excerpt of the linked content" 24 26 }, 25 27 "thumb": { ··· 34 36 }, 35 37 "embedType": { 36 38 "type": "string", 37 - "enum": ["article", "image", "video-stream"], 39 + "knownValues": ["article", "image", "video-stream"], 38 40 "description": "Type hint for special handling of known providers" 39 41 }, 40 42 "provider": {
+1
internal/atproto/lexicon/social/coves/embed/images.json
··· 33 33 "alt": { 34 34 "type": "string", 35 35 "maxLength": 1000, 36 + "maxGraphemes": 1000, 36 37 "description": "Alt text for accessibility" 37 38 }, 38 39 "aspectRatio": {
+1 -4
internal/atproto/lexicon/social/coves/embed/video.json
··· 22 22 "alt": { 23 23 "type": "string", 24 24 "maxLength": 1000, 25 + "maxGraphemes": 1000, 25 26 "description": "Alt text describing video content" 26 27 }, 27 28 "duration": { 28 29 "type": "integer", 29 30 "minimum": 0, 30 31 "description": "Duration in seconds" 31 - }, 32 - "aspectRatio": { 33 - "type": "ref", 34 - "ref": "social.coves.embed.image#aspectRatio" 35 32 } 36 33 } 37 34 }
-32
internal/atproto/lexicon/social/coves/federation/post.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "social.coves.federation.post", 4 - "defs": { 5 - "main": { 6 - "type": "object", 7 - "description": "Reference to original federated post", 8 - "required": ["platform", "uri"], 9 - "properties": { 10 - "platform": { 11 - "type": "string", 12 - "knownValues": ["bluesky", "lemmy", "atproto"], 13 - "description": "Platform the post originated from" 14 - }, 15 - "uri": { 16 - "type": "string", 17 - "format": "uri", 18 - "description": "Original URI of the post (at:// URI for atproto platforms)" 19 - }, 20 - "id": { 21 - "type": "string", 22 - "description": "Platform-specific post ID" 23 - }, 24 - "originalCreatedAt": { 25 - "type": "string", 26 - "format": "datetime", 27 - "description": "Timestamp when originally posted on source platform" 28 - } 29 - } 30 - } 31 - } 32 - }
+5 -5
internal/atproto/lexicon/social/coves/feed/getAll.json
··· 10 10 "properties": { 11 11 "sort": { 12 12 "type": "string", 13 - "enum": ["hot", "top", "new"], 13 + "knownValues": ["hot", "top", "new"], 14 14 "default": "hot", 15 15 "description": "Sort order for global feed" 16 16 }, 17 17 "postType": { 18 18 "type": "string", 19 - "enum": ["text", "article", "image", "video", "microblog"], 19 + "knownValues": ["text", "article", "image", "video", "microblog"], 20 20 "description": "Filter by a single post type (computed from embed structure)" 21 21 }, 22 22 "postTypes": { 23 23 "type": "array", 24 24 "items": { 25 25 "type": "string", 26 - "enum": ["text", "article", "image", "video", "microblog"] 26 + "knownValues": ["text", "article", "image", "video", "microblog"] 27 27 }, 28 28 "description": "Filter by multiple post types (computed from embed structure)" 29 29 }, 30 30 "timeframe": { 31 31 "type": "string", 32 - "enum": ["hour", "day", "week", "month", "year", "all"], 32 + "knownValues": ["hour", "day", "week", "month", "year", "all"], 33 33 "default": "day", 34 34 "description": "Timeframe for top sorting (only applies when sort=top)" 35 35 }, ··· 54 54 "type": "array", 55 55 "items": { 56 56 "type": "ref", 57 - "ref": "social.coves.feed.getTimeline#feedViewPost" 57 + "ref": "social.coves.feed.defs#feedViewPost" 58 58 } 59 59 }, 60 60 "cursor": {
+3 -3
internal/atproto/lexicon/social/coves/feed/getCommunity.json
··· 16 16 }, 17 17 "sort": { 18 18 "type": "string", 19 - "enum": ["hot", "top", "new"], 19 + "knownValues": ["hot", "top", "new"], 20 20 "default": "hot", 21 21 "description": "Sort order for community feed" 22 22 }, 23 23 "timeframe": { 24 24 "type": "string", 25 - "enum": ["hour", "day", "week", "month", "year", "all"], 25 + "knownValues": ["hour", "day", "week", "month", "year", "all"], 26 26 "default": "day", 27 27 "description": "Timeframe for top sorting (only applies when sort=top)" 28 28 }, ··· 47 47 "type": "array", 48 48 "items": { 49 49 "type": "ref", 50 - "ref": "social.coves.feed.getTimeline#feedViewPost" 50 + "ref": "social.coves.feed.defs#feedViewPost" 51 51 } 52 52 }, 53 53 "cursor": {
+2 -2
internal/atproto/lexicon/social/coves/feed/getDiscover.json
··· 10 10 "properties": { 11 11 "sort": { 12 12 "type": "string", 13 - "enum": ["hot", "top", "new"], 13 + "knownValues": ["hot", "top", "new"], 14 14 "default": "hot", 15 15 "description": "Sort order for discover feed" 16 16 }, 17 17 "timeframe": { 18 18 "type": "string", 19 - "enum": ["hour", "day", "week", "month", "year", "all"], 19 + "knownValues": ["hour", "day", "week", "month", "year", "all"], 20 20 "default": "day", 21 21 "description": "Timeframe for top sorting (only applies when sort=top)" 22 22 },
+2 -2
internal/atproto/lexicon/social/coves/feed/getTimeline.json
··· 10 10 "properties": { 11 11 "sort": { 12 12 "type": "string", 13 - "enum": ["hot", "top", "new"], 13 + "knownValues": ["hot", "top", "new"], 14 14 "default": "hot", 15 15 "description": "Sort order for timeline feed" 16 16 }, 17 17 "timeframe": { 18 18 "type": "string", 19 - "enum": ["hour", "day", "week", "month", "year", "all"], 19 + "knownValues": ["hour", "day", "week", "month", "year", "all"], 20 20 "default": "day", 21 21 "description": "Timeframe for top sorting (only applies when sort=top)" 22 22 },
-31
internal/atproto/lexicon/social/coves/interaction/share.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "social.coves.interaction.share", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "description": "Sharing a post to another community or platform", 8 - "key": "tid", 9 - "record": { 10 - "type": "object", 11 - "required": ["subject", "createdAt"], 12 - "properties": { 13 - "subject": { 14 - "type": "string", 15 - "format": "at-uri", 16 - "description": "AT-URI of the post being shared" 17 - }, 18 - "toCommunity": { 19 - "type": "string", 20 - "format": "at-uri", 21 - "description": "Community being shared to (if applicable)" 22 - }, 23 - "createdAt": { 24 - "type": "string", 25 - "format": "datetime" 26 - } 27 - } 28 - } 29 - } 30 - } 31 - }
-33
internal/atproto/lexicon/social/coves/interaction/tag.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "social.coves.interaction.tag", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "description": "A tag applied to a post or comment", 8 - "key": "tid", 9 - "record": { 10 - "type": "object", 11 - "required": ["subject", "tag", "createdAt"], 12 - "properties": { 13 - "subject": { 14 - "type": "string", 15 - "format": "at-uri", 16 - "description": "AT-URI of the post or comment being tagged" 17 - }, 18 - "tag": { 19 - "type": "string", 20 - "minLength": 1, 21 - "maxLength": 50, 22 - "knownValues": ["helpful", "insightful", "spam", "hostile", "offtopic", "misleading"], 23 - "description": "Predefined tag or custom community tag" 24 - }, 25 - "createdAt": { 26 - "type": "string", 27 - "format": "datetime" 28 - } 29 - } 30 - } 31 - } 32 - } 33 - }
+13
scripts/validate-schemas.sh
··· 1 + #!/bin/bash 2 + # Validate all lexicon schemas and test data 3 + 4 + set -e 5 + 6 + echo "🔍 Validating Coves lexicon schemas..." 7 + echo "" 8 + 9 + # Run the Go validation tool 10 + go run ./cmd/validate-lexicon/main.go 11 + 12 + echo "" 13 + echo "✅ Schema validation complete!"
-9
tests/lexicon-test-data/community/moderator-invalid-permissions.json
··· 1 - { 2 - "$type": "social.coves.community.moderator", 3 - "user": "did:plc:moderator123", 4 - "community": "did:plc:community123", 5 - "role": "moderator", 6 - "permissions": ["remove_posts", "invalid-permission"], 7 - "createdAt": "2024-06-15T10:00:00Z", 8 - "createdBy": "did:plc:owner123" 9 - }
-5
tests/lexicon-test-data/interaction/share-valid-no-community.json
··· 1 - { 2 - "$type": "social.coves.interaction.share", 3 - "subject": "at://did:plc:originalauthor/social.coves.post.record/3k7a3dmb5bk2c", 4 - "createdAt": "2025-01-09T17:00:00Z" 5 - }
-6
tests/lexicon-test-data/interaction/share-valid.json
··· 1 - { 2 - "$type": "social.coves.interaction.share", 3 - "subject": "at://did:plc:originalauthor/social.coves.post.record/3k7a3dmb5bk2c", 4 - "community": "did:plc:targetcommunity", 5 - "createdAt": "2025-01-09T17:00:00Z" 6 - }
-6
tests/lexicon-test-data/interaction/tag-invalid-empty.json
··· 1 - { 2 - "$type": "social.coves.interaction.tag", 3 - "subject": "at://did:plc:author123/social.coves.post.record/3k7a3dmb5bk2c", 4 - "tag": "", 5 - "createdAt": "2025-01-09T17:15:00Z" 6 - }
-6
tests/lexicon-test-data/interaction/tag-valid-custom.json
··· 1 - { 2 - "$type": "social.coves.interaction.tag", 3 - "subject": "at://did:plc:author123/social.coves.post.record/3k7a3dmb5bk2c", 4 - "tag": "beginner-friendly", 5 - "createdAt": "2025-01-09T17:15:00Z" 6 - }
-6
tests/lexicon-test-data/interaction/tag-valid-known.json
··· 1 - { 2 - "$type": "social.coves.interaction.tag", 3 - "subject": "at://did:plc:author123/social.coves.post.record/3k7a3dmb5bk2c", 4 - "tag": "nsfw", 5 - "createdAt": "2025-01-09T17:15:00Z" 6 - }
+1 -1
tests/lexicon-test-data/moderation/tribunal-vote-valid.json
··· 1 1 { 2 2 "$type": "social.coves.moderation.tribunalVote", 3 3 "tribunal": "at://did:plc:community123/social.coves.moderation.tribunal/3k7a3dmb5bk2c", 4 - "subject": "at://$1/social.coves.community.post/3k7a2clb4bj2b", 4 + "subject": "at://did:plc:testuser123/social.coves.community.post/3k7a2clb4bj2b", 5 5 "decision": "remove", 6 6 "reasoning": "The moderator's action was justified based on clear violation of Rule 2 (No Spam). The user posted the same promotional content across multiple communities within a short timeframe.", 7 7 "precedents": [
-11
tests/lexicon-test-data/post/post-invalid-enum-type.json
··· 1 - { 2 - "$type": "social.coves.community.post", 3 - "community": "did:plc:programming123", 4 - "author": "did:plc:testauthor123", 5 - "postType": "invalid-type", 6 - "title": "This has an invalid post type", 7 - "content": "The postType field is not defined in the schema and should be rejected", 8 - "tags": [], 9 - "langs": ["en"], 10 - "createdAt": "2025-01-09T14:30:00Z" 11 - }