[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

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

fix(deps): remove dependency on `@atproto/api` (#1295)

Co-authored-by: Daniel Roe <daniel@roe.dev>

authored by

Matthieu Sieben
Daniel Roe
and committed by
GitHub
ede1b599 f431b91c

+3525 -156
+9 -14
app/components/Header/AuthModal.client.vue
··· 1 1 <script setup lang="ts"> 2 2 import { useAtproto } from '~/composables/atproto/useAtproto' 3 3 import { authRedirect } from '~/utils/atproto/helpers' 4 - import { ensureValidAtIdentifier } from '@atproto/syntax' 4 + import { isAtIdentifierString } from '@atproto/lex' 5 5 6 6 const handleInput = shallowRef('') 7 7 const errorMessage = shallowRef('') ··· 28 28 29 29 async function handleLogin() { 30 30 if (handleInput.value) { 31 - // URLS to PDSs are valid for oauth redirects 32 - if (!handleInput.value.startsWith('https://')) { 33 - try { 34 - ensureValidAtIdentifier(handleInput.value) 35 - } catch (error) { 36 - errorMessage.value = 37 - error instanceof Error ? error.message : $t('auth.modal.default_input_error') 38 - return 39 - } 31 + // URLS to PDSs are valid for initiating oauth flows 32 + if (handleInput.value.startsWith('https://') || isAtIdentifierString(handleInput.value)) { 33 + await authRedirect(handleInput.value, { 34 + redirectTo: route.fullPath, 35 + locale: locale.value, 36 + }) 37 + } else { 38 + errorMessage.value = $t('auth.modal.default_input_error') 40 39 } 41 - await authRedirect(handleInput.value, { 42 - redirectTo: route.fullPath, 43 - locale: locale.value, 44 - }) 45 40 } 46 41 } 47 42
+105 -1
lexicons.json
··· 1 1 { 2 2 "version": 1, 3 - "lexicons": ["app.bsky.actor.profile", "site.standard.document"], 3 + "lexicons": [ 4 + "app.bsky.actor.getProfiles", 5 + "app.bsky.actor.profile", 6 + "app.bsky.embed.external", 7 + "app.bsky.embed.images", 8 + "app.bsky.feed.defs", 9 + "app.bsky.feed.getLikes", 10 + "app.bsky.feed.getPostThread", 11 + "app.bsky.feed.getPosts", 12 + "app.bsky.feed.post", 13 + "com.bad-example.identity.resolveMiniDoc", 14 + "site.standard.document" 15 + ], 4 16 "resolutions": { 17 + "app.bsky.actor.defs": { 18 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.defs", 19 + "cid": "bafyreigwqwhe2jxohagozazfbrf6dxgzphvkg3d3lg7uxdvepsimqyclka" 20 + }, 21 + "app.bsky.actor.getProfiles": { 22 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.getProfiles", 23 + "cid": "bafyreihphrndnt2cvnv3nk442f6kohdnpagk4xip74f2jn36uoal25vhta" 24 + }, 5 25 "app.bsky.actor.profile": { 6 26 "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.profile", 7 27 "cid": "bafyreia6umzg3a6d7mjbow4p57tviey45muohklhgsvjoamcctoiusr4pe" 8 28 }, 29 + "app.bsky.actor.status": { 30 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.status", 31 + "cid": "bafyreifdg4b64wohpwkh5lydc6tckvol2rspnpni6dec6recy2rhvlnz4a" 32 + }, 33 + "app.bsky.embed.defs": { 34 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.defs", 35 + "cid": "bafyreia42uud4qil67wknywzbxfyxc3b7woewsii54cakq2ould3ldetei" 36 + }, 37 + "app.bsky.embed.external": { 38 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.external", 39 + "cid": "bafyreiblxmpzgwg4fbr45b4xzts3h4k72k7cdnrxy2ub2w5d7mnwzznkwi" 40 + }, 41 + "app.bsky.embed.images": { 42 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.images", 43 + "cid": "bafyreifrntpx63uebiskpooozv6hji62swectq3pocw5h5gpkkqynmazdm" 44 + }, 45 + "app.bsky.embed.record": { 46 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.record", 47 + "cid": "bafyreigdtmu53blwxoygphg5zh5zpmlftz64c3jyqpv2yqpx3nrichkyla" 48 + }, 49 + "app.bsky.embed.recordWithMedia": { 50 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.recordWithMedia", 51 + "cid": "bafyreia7jrw2p73egm7vrunssgzeyj2rwmk3s4dymfhgzcavxjfaje3qfi" 52 + }, 53 + "app.bsky.embed.video": { 54 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.video", 55 + "cid": "bafyreie3nug4ezpwodl6yrpgv5edkazzn22t7ea4yaeuun4rctyekkngai" 56 + }, 57 + "app.bsky.feed.defs": { 58 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.defs", 59 + "cid": "bafyreiadwvxawxifsnm7ae6l56aq23qs7ndih7npgs6pxmkoin7gi3k6pu" 60 + }, 61 + "app.bsky.feed.getLikes": { 62 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.getLikes", 63 + "cid": "bafyreib6ylalknwxizbtbwsdmja4rhjswwczut5ivjdavfgew27m7knqou" 64 + }, 65 + "app.bsky.feed.getPostThread": { 66 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.getPostThread", 67 + "cid": "bafyreif37upfzfu7t4rv3pqzz3swu2oc4nrxdxfr3xqjsrrmgyckneitta" 68 + }, 69 + "app.bsky.feed.getPosts": { 70 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.getPosts", 71 + "cid": "bafyreihy4tanaizkynghab46n3qztsspvoaqtbrkzinvxi4igeylg5ghji" 72 + }, 73 + "app.bsky.feed.post": { 74 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.post", 75 + "cid": "bafyreidgbehqwweghrrddfu6jgj7lyr6fwhzgazhirnszdb5lvr7iynkiy" 76 + }, 77 + "app.bsky.feed.postgate": { 78 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.postgate", 79 + "cid": "bafyreiai5efexyluyptv5tbl6kqbqlnneczqzexcqnxmitmulyjfaftgva" 80 + }, 81 + "app.bsky.feed.threadgate": { 82 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.threadgate", 83 + "cid": "bafyreiht77wd6duduz4yqp62m6dwma5dy7gdihps4g2nd73acfzqlglvdi" 84 + }, 85 + "app.bsky.graph.defs": { 86 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.graph.defs", 87 + "cid": "bafyreifcipomli7yggtl46xufgxlnrw7se6xmsdxmzgfcz2tiu76ljatxm" 88 + }, 89 + "app.bsky.labeler.defs": { 90 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.labeler.defs", 91 + "cid": "bafyreicxx5i36v5dbqk5vvfzhnta5gajrvc544mnepux4wksrkid7mw3q4" 92 + }, 93 + "app.bsky.notification.defs": { 94 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.notification.defs", 95 + "cid": "bafyreickbpnayydlyfakliahgf23jjuesllh6qrslyofk5yz5xizjavhui" 96 + }, 97 + "app.bsky.richtext.facet": { 98 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.richtext.facet", 99 + "cid": "bafyreidg56eo7zynf6ihz4xb627vwoqf5idnevkmwp7sxc4tijg6xngbu4" 100 + }, 9 101 "com.atproto.label.defs": { 10 102 "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.label.defs", 11 103 "cid": "bafyreig4hmnb2xkecyg4aaqfhr2rrcxxb3gsr4xks4rqb7rscrycalbrji" 12 104 }, 105 + "com.atproto.moderation.defs": { 106 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.moderation.defs", 107 + "cid": "bafyreideawy4rlpgces2oebk5q4kpurbonhb5qtl4pes7dvxsc5osaiksy" 108 + }, 13 109 "com.atproto.repo.strongRef": { 14 110 "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.strongRef", 15 111 "cid": "bafyreifrkdbnkvfjujntdaeigolnrjj3srrs53tfixjhmacclps72qlov4" 16 112 }, 113 + "com.bad-example.identity.resolveMiniDoc": { 114 + "uri": "at://did:plc:hdhoaan3xa3jiuq4fg4mefid/com.atproto.lexicon.schema/com.bad-example.identity.resolveMiniDoc", 115 + "cid": "bafyreihqq4ud3ihj63op2ety24nziyyh7axibm65mnde5bdeai2z6adety" 116 + }, 17 117 "site.standard.document": { 18 118 "uri": "at://did:plc:re3ebnp5v7ffagz6rb6xfei4/com.atproto.lexicon.schema/site.standard.document", 19 119 "cid": "bafyreigdukg62hmel4jbdvghdsoaphslhrdktmahzdyokomuka6yejetwa" 120 + }, 121 + "tools.ozone.report.defs": { 122 + "uri": "at://did:plc:33dt5kftu3jq2h5h4jjlqezt/com.atproto.lexicon.schema/tools.ozone.report.defs", 123 + "cid": "bafyreic3l2rmh2ugirt3jz372wcvy333m7t2ynlyzj2k54oshijs6lxdfu" 20 124 } 21 125 } 22 126 }
+845
lexicons/app/bsky/actor/defs.json
··· 1 + { 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 + "cid": { 89 + "type": "string", 90 + "format": "cid" 91 + }, 92 + "uri": { 93 + "type": "string", 94 + "format": "at-uri" 95 + }, 96 + "embed": { 97 + "refs": ["app.bsky.embed.external#view"], 98 + "type": "union", 99 + "description": "An optional embed associated with the status." 100 + }, 101 + "record": { 102 + "type": "unknown" 103 + }, 104 + "status": { 105 + "type": "string", 106 + "description": "The status for the account.", 107 + "knownValues": ["app.bsky.actor.status#live"] 108 + }, 109 + "isActive": { 110 + "type": "boolean", 111 + "description": "True if the status is not expired, false if it is expired. Only present if expiration was set." 112 + }, 113 + "expiresAt": { 114 + "type": "string", 115 + "format": "datetime", 116 + "description": "The date when this status will expire. The application might choose to no longer return the status after expiration." 117 + }, 118 + "isDisabled": { 119 + "type": "boolean", 120 + "description": "True if the user's go-live access has been disabled by a moderator, false otherwise." 121 + } 122 + } 123 + }, 124 + "preferences": { 125 + "type": "array", 126 + "items": { 127 + "refs": [ 128 + "#adultContentPref", 129 + "#contentLabelPref", 130 + "#savedFeedsPref", 131 + "#savedFeedsPrefV2", 132 + "#personalDetailsPref", 133 + "#declaredAgePref", 134 + "#feedViewPref", 135 + "#threadViewPref", 136 + "#interestsPref", 137 + "#mutedWordsPref", 138 + "#hiddenPostsPref", 139 + "#bskyAppStatePref", 140 + "#labelersPref", 141 + "#postInteractionSettingsPref", 142 + "#verificationPrefs", 143 + "#liveEventPreferences" 144 + ], 145 + "type": "union" 146 + } 147 + }, 148 + "profileView": { 149 + "type": "object", 150 + "required": ["did", "handle"], 151 + "properties": { 152 + "did": { 153 + "type": "string", 154 + "format": "did" 155 + }, 156 + "debug": { 157 + "type": "unknown", 158 + "description": "Debug information for internal development" 159 + }, 160 + "avatar": { 161 + "type": "string", 162 + "format": "uri" 163 + }, 164 + "handle": { 165 + "type": "string", 166 + "format": "handle" 167 + }, 168 + "labels": { 169 + "type": "array", 170 + "items": { 171 + "ref": "com.atproto.label.defs#label", 172 + "type": "ref" 173 + } 174 + }, 175 + "status": { 176 + "ref": "#statusView", 177 + "type": "ref" 178 + }, 179 + "viewer": { 180 + "ref": "#viewerState", 181 + "type": "ref" 182 + }, 183 + "pronouns": { 184 + "type": "string" 185 + }, 186 + "createdAt": { 187 + "type": "string", 188 + "format": "datetime" 189 + }, 190 + "indexedAt": { 191 + "type": "string", 192 + "format": "datetime" 193 + }, 194 + "associated": { 195 + "ref": "#profileAssociated", 196 + "type": "ref" 197 + }, 198 + "description": { 199 + "type": "string", 200 + "maxLength": 2560, 201 + "maxGraphemes": 256 202 + }, 203 + "displayName": { 204 + "type": "string", 205 + "maxLength": 640, 206 + "maxGraphemes": 64 207 + }, 208 + "verification": { 209 + "ref": "#verificationState", 210 + "type": "ref" 211 + } 212 + } 213 + }, 214 + "viewerState": { 215 + "type": "object", 216 + "properties": { 217 + "muted": { 218 + "type": "boolean" 219 + }, 220 + "blocking": { 221 + "type": "string", 222 + "format": "at-uri" 223 + }, 224 + "blockedBy": { 225 + "type": "boolean" 226 + }, 227 + "following": { 228 + "type": "string", 229 + "format": "at-uri" 230 + }, 231 + "followedBy": { 232 + "type": "string", 233 + "format": "at-uri" 234 + }, 235 + "mutedByList": { 236 + "ref": "app.bsky.graph.defs#listViewBasic", 237 + "type": "ref" 238 + }, 239 + "blockingByList": { 240 + "ref": "app.bsky.graph.defs#listViewBasic", 241 + "type": "ref" 242 + }, 243 + "knownFollowers": { 244 + "ref": "#knownFollowers", 245 + "type": "ref", 246 + "description": "This property is present only in selected cases, as an optimization." 247 + }, 248 + "activitySubscription": { 249 + "ref": "app.bsky.notification.defs#activitySubscription", 250 + "type": "ref", 251 + "description": "This property is present only in selected cases, as an optimization." 252 + } 253 + }, 254 + "description": "Metadata about the requesting account's relationship with the subject account. Only has meaningful content for authed requests." 255 + }, 256 + "feedViewPref": { 257 + "type": "object", 258 + "required": ["feed"], 259 + "properties": { 260 + "feed": { 261 + "type": "string", 262 + "description": "The URI of the feed, or an identifier which describes the feed." 263 + }, 264 + "hideReplies": { 265 + "type": "boolean", 266 + "description": "Hide replies in the feed." 267 + }, 268 + "hideReposts": { 269 + "type": "boolean", 270 + "description": "Hide reposts in the feed." 271 + }, 272 + "hideQuotePosts": { 273 + "type": "boolean", 274 + "description": "Hide quote posts in the feed." 275 + }, 276 + "hideRepliesByLikeCount": { 277 + "type": "integer", 278 + "description": "Hide replies in the feed if they do not have this number of likes." 279 + }, 280 + "hideRepliesByUnfollowed": { 281 + "type": "boolean", 282 + "default": true, 283 + "description": "Hide replies in the feed if they are not by followed users." 284 + } 285 + } 286 + }, 287 + "labelersPref": { 288 + "type": "object", 289 + "required": ["labelers"], 290 + "properties": { 291 + "labelers": { 292 + "type": "array", 293 + "items": { 294 + "ref": "#labelerPrefItem", 295 + "type": "ref" 296 + } 297 + } 298 + } 299 + }, 300 + "interestsPref": { 301 + "type": "object", 302 + "required": ["tags"], 303 + "properties": { 304 + "tags": { 305 + "type": "array", 306 + "items": { 307 + "type": "string", 308 + "maxLength": 640, 309 + "maxGraphemes": 64 310 + }, 311 + "maxLength": 100, 312 + "description": "A list of tags which describe the account owner's interests gathered during onboarding." 313 + } 314 + } 315 + }, 316 + "knownFollowers": { 317 + "type": "object", 318 + "required": ["count", "followers"], 319 + "properties": { 320 + "count": { 321 + "type": "integer" 322 + }, 323 + "followers": { 324 + "type": "array", 325 + "items": { 326 + "ref": "#profileViewBasic", 327 + "type": "ref" 328 + }, 329 + "maxLength": 5, 330 + "minLength": 0 331 + } 332 + }, 333 + "description": "The subject's followers whom you also follow" 334 + }, 335 + "mutedWordsPref": { 336 + "type": "object", 337 + "required": ["items"], 338 + "properties": { 339 + "items": { 340 + "type": "array", 341 + "items": { 342 + "ref": "app.bsky.actor.defs#mutedWord", 343 + "type": "ref" 344 + }, 345 + "description": "A list of words the account owner has muted." 346 + } 347 + } 348 + }, 349 + "savedFeedsPref": { 350 + "type": "object", 351 + "required": ["pinned", "saved"], 352 + "properties": { 353 + "saved": { 354 + "type": "array", 355 + "items": { 356 + "type": "string", 357 + "format": "at-uri" 358 + } 359 + }, 360 + "pinned": { 361 + "type": "array", 362 + "items": { 363 + "type": "string", 364 + "format": "at-uri" 365 + } 366 + }, 367 + "timelineIndex": { 368 + "type": "integer" 369 + } 370 + } 371 + }, 372 + "threadViewPref": { 373 + "type": "object", 374 + "properties": { 375 + "sort": { 376 + "type": "string", 377 + "description": "Sorting mode for threads.", 378 + "knownValues": ["oldest", "newest", "most-likes", "random", "hotness"] 379 + } 380 + } 381 + }, 382 + "declaredAgePref": { 383 + "type": "object", 384 + "properties": { 385 + "isOverAge13": { 386 + "type": "boolean", 387 + "description": "Indicates if the user has declared that they are over 13 years of age." 388 + }, 389 + "isOverAge16": { 390 + "type": "boolean", 391 + "description": "Indicates if the user has declared that they are over 16 years of age." 392 + }, 393 + "isOverAge18": { 394 + "type": "boolean", 395 + "description": "Indicates if the user has declared that they are over 18 years of age." 396 + } 397 + }, 398 + "description": "Read-only preference containing value(s) inferred from the user's declared birthdate. Absence of this preference object in the response indicates that the user has not made a declaration." 399 + }, 400 + "hiddenPostsPref": { 401 + "type": "object", 402 + "required": ["items"], 403 + "properties": { 404 + "items": { 405 + "type": "array", 406 + "items": { 407 + "type": "string", 408 + "format": "at-uri" 409 + }, 410 + "description": "A list of URIs of posts the account owner has hidden." 411 + } 412 + } 413 + }, 414 + "labelerPrefItem": { 415 + "type": "object", 416 + "required": ["did"], 417 + "properties": { 418 + "did": { 419 + "type": "string", 420 + "format": "did" 421 + } 422 + } 423 + }, 424 + "mutedWordTarget": { 425 + "type": "string", 426 + "maxLength": 640, 427 + "knownValues": ["content", "tag"], 428 + "maxGraphemes": 64 429 + }, 430 + "adultContentPref": { 431 + "type": "object", 432 + "required": ["enabled"], 433 + "properties": { 434 + "enabled": { 435 + "type": "boolean", 436 + "default": false 437 + } 438 + } 439 + }, 440 + "bskyAppStatePref": { 441 + "type": "object", 442 + "properties": { 443 + "nuxs": { 444 + "type": "array", 445 + "items": { 446 + "ref": "app.bsky.actor.defs#nux", 447 + "type": "ref" 448 + }, 449 + "maxLength": 100, 450 + "description": "Storage for NUXs the user has encountered." 451 + }, 452 + "queuedNudges": { 453 + "type": "array", 454 + "items": { 455 + "type": "string", 456 + "maxLength": 100 457 + }, 458 + "maxLength": 1000, 459 + "description": "An array of tokens which identify nudges (modals, popups, tours, highlight dots) that should be shown to the user." 460 + }, 461 + "activeProgressGuide": { 462 + "ref": "#bskyAppProgressGuide", 463 + "type": "ref" 464 + } 465 + }, 466 + "description": "A grab bag of state that's specific to the bsky.app program. Third-party apps shouldn't use this." 467 + }, 468 + "contentLabelPref": { 469 + "type": "object", 470 + "required": ["label", "visibility"], 471 + "properties": { 472 + "label": { 473 + "type": "string" 474 + }, 475 + "labelerDid": { 476 + "type": "string", 477 + "format": "did", 478 + "description": "Which labeler does this preference apply to? If undefined, applies globally." 479 + }, 480 + "visibility": { 481 + "type": "string", 482 + "knownValues": ["ignore", "show", "warn", "hide"] 483 + } 484 + } 485 + }, 486 + "profileViewBasic": { 487 + "type": "object", 488 + "required": ["did", "handle"], 489 + "properties": { 490 + "did": { 491 + "type": "string", 492 + "format": "did" 493 + }, 494 + "debug": { 495 + "type": "unknown", 496 + "description": "Debug information for internal development" 497 + }, 498 + "avatar": { 499 + "type": "string", 500 + "format": "uri" 501 + }, 502 + "handle": { 503 + "type": "string", 504 + "format": "handle" 505 + }, 506 + "labels": { 507 + "type": "array", 508 + "items": { 509 + "ref": "com.atproto.label.defs#label", 510 + "type": "ref" 511 + } 512 + }, 513 + "status": { 514 + "ref": "#statusView", 515 + "type": "ref" 516 + }, 517 + "viewer": { 518 + "ref": "#viewerState", 519 + "type": "ref" 520 + }, 521 + "pronouns": { 522 + "type": "string" 523 + }, 524 + "createdAt": { 525 + "type": "string", 526 + "format": "datetime" 527 + }, 528 + "associated": { 529 + "ref": "#profileAssociated", 530 + "type": "ref" 531 + }, 532 + "displayName": { 533 + "type": "string", 534 + "maxLength": 640, 535 + "maxGraphemes": 64 536 + }, 537 + "verification": { 538 + "ref": "#verificationState", 539 + "type": "ref" 540 + } 541 + } 542 + }, 543 + "savedFeedsPrefV2": { 544 + "type": "object", 545 + "required": ["items"], 546 + "properties": { 547 + "items": { 548 + "type": "array", 549 + "items": { 550 + "ref": "app.bsky.actor.defs#savedFeed", 551 + "type": "ref" 552 + } 553 + } 554 + } 555 + }, 556 + "verificationView": { 557 + "type": "object", 558 + "required": ["issuer", "uri", "isValid", "createdAt"], 559 + "properties": { 560 + "uri": { 561 + "type": "string", 562 + "format": "at-uri", 563 + "description": "The AT-URI of the verification record." 564 + }, 565 + "issuer": { 566 + "type": "string", 567 + "format": "did", 568 + "description": "The user who issued this verification." 569 + }, 570 + "isValid": { 571 + "type": "boolean", 572 + "description": "True if the verification passes validation, otherwise false." 573 + }, 574 + "createdAt": { 575 + "type": "string", 576 + "format": "datetime", 577 + "description": "Timestamp when the verification was created." 578 + } 579 + }, 580 + "description": "An individual verification for an associated subject." 581 + }, 582 + "profileAssociated": { 583 + "type": "object", 584 + "properties": { 585 + "chat": { 586 + "ref": "#profileAssociatedChat", 587 + "type": "ref" 588 + }, 589 + "germ": { 590 + "ref": "#profileAssociatedGerm", 591 + "type": "ref" 592 + }, 593 + "lists": { 594 + "type": "integer" 595 + }, 596 + "labeler": { 597 + "type": "boolean" 598 + }, 599 + "feedgens": { 600 + "type": "integer" 601 + }, 602 + "starterPacks": { 603 + "type": "integer" 604 + }, 605 + "activitySubscription": { 606 + "ref": "#profileAssociatedActivitySubscription", 607 + "type": "ref" 608 + } 609 + } 610 + }, 611 + "verificationPrefs": { 612 + "type": "object", 613 + "required": [], 614 + "properties": { 615 + "hideBadges": { 616 + "type": "boolean", 617 + "default": false, 618 + "description": "Hide the blue check badges for verified accounts and trusted verifiers." 619 + } 620 + }, 621 + "description": "Preferences for how verified accounts appear in the app." 622 + }, 623 + "verificationState": { 624 + "type": "object", 625 + "required": ["verifications", "verifiedStatus", "trustedVerifierStatus"], 626 + "properties": { 627 + "verifications": { 628 + "type": "array", 629 + "items": { 630 + "ref": "#verificationView", 631 + "type": "ref" 632 + }, 633 + "description": "All verifications issued by trusted verifiers on behalf of this user. Verifications by untrusted verifiers are not included." 634 + }, 635 + "verifiedStatus": { 636 + "type": "string", 637 + "description": "The user's status as a verified account.", 638 + "knownValues": ["valid", "invalid", "none"] 639 + }, 640 + "trustedVerifierStatus": { 641 + "type": "string", 642 + "description": "The user's status as a trusted verifier.", 643 + "knownValues": ["valid", "invalid", "none"] 644 + } 645 + }, 646 + "description": "Represents the verification information about the user this object is attached to." 647 + }, 648 + "personalDetailsPref": { 649 + "type": "object", 650 + "properties": { 651 + "birthDate": { 652 + "type": "string", 653 + "format": "datetime", 654 + "description": "The birth date of account owner." 655 + } 656 + } 657 + }, 658 + "profileViewDetailed": { 659 + "type": "object", 660 + "required": ["did", "handle"], 661 + "properties": { 662 + "did": { 663 + "type": "string", 664 + "format": "did" 665 + }, 666 + "debug": { 667 + "type": "unknown", 668 + "description": "Debug information for internal development" 669 + }, 670 + "avatar": { 671 + "type": "string", 672 + "format": "uri" 673 + }, 674 + "banner": { 675 + "type": "string", 676 + "format": "uri" 677 + }, 678 + "handle": { 679 + "type": "string", 680 + "format": "handle" 681 + }, 682 + "labels": { 683 + "type": "array", 684 + "items": { 685 + "ref": "com.atproto.label.defs#label", 686 + "type": "ref" 687 + } 688 + }, 689 + "status": { 690 + "ref": "#statusView", 691 + "type": "ref" 692 + }, 693 + "viewer": { 694 + "ref": "#viewerState", 695 + "type": "ref" 696 + }, 697 + "website": { 698 + "type": "string", 699 + "format": "uri" 700 + }, 701 + "pronouns": { 702 + "type": "string" 703 + }, 704 + "createdAt": { 705 + "type": "string", 706 + "format": "datetime" 707 + }, 708 + "indexedAt": { 709 + "type": "string", 710 + "format": "datetime" 711 + }, 712 + "associated": { 713 + "ref": "#profileAssociated", 714 + "type": "ref" 715 + }, 716 + "pinnedPost": { 717 + "ref": "com.atproto.repo.strongRef", 718 + "type": "ref" 719 + }, 720 + "postsCount": { 721 + "type": "integer" 722 + }, 723 + "description": { 724 + "type": "string", 725 + "maxLength": 2560, 726 + "maxGraphemes": 256 727 + }, 728 + "displayName": { 729 + "type": "string", 730 + "maxLength": 640, 731 + "maxGraphemes": 64 732 + }, 733 + "followsCount": { 734 + "type": "integer" 735 + }, 736 + "verification": { 737 + "ref": "#verificationState", 738 + "type": "ref" 739 + }, 740 + "followersCount": { 741 + "type": "integer" 742 + }, 743 + "joinedViaStarterPack": { 744 + "ref": "app.bsky.graph.defs#starterPackViewBasic", 745 + "type": "ref" 746 + } 747 + } 748 + }, 749 + "bskyAppProgressGuide": { 750 + "type": "object", 751 + "required": ["guide"], 752 + "properties": { 753 + "guide": { 754 + "type": "string", 755 + "maxLength": 100 756 + } 757 + }, 758 + "description": "If set, an active progress guide. Once completed, can be set to undefined. Should have unspecced fields tracking progress." 759 + }, 760 + "liveEventPreferences": { 761 + "type": "object", 762 + "properties": { 763 + "hideAllFeeds": { 764 + "type": "boolean", 765 + "default": false, 766 + "description": "Whether to hide all feeds from live events." 767 + }, 768 + "hiddenFeedIds": { 769 + "type": "array", 770 + "items": { 771 + "type": "string" 772 + }, 773 + "description": "A list of feed IDs that the user has hidden from live events." 774 + } 775 + }, 776 + "description": "Preferences for live events." 777 + }, 778 + "profileAssociatedChat": { 779 + "type": "object", 780 + "required": ["allowIncoming"], 781 + "properties": { 782 + "allowIncoming": { 783 + "type": "string", 784 + "knownValues": ["all", "none", "following"] 785 + } 786 + } 787 + }, 788 + "profileAssociatedGerm": { 789 + "type": "object", 790 + "required": ["showButtonTo", "messageMeUrl"], 791 + "properties": { 792 + "messageMeUrl": { 793 + "type": "string", 794 + "format": "uri" 795 + }, 796 + "showButtonTo": { 797 + "type": "string", 798 + "knownValues": ["usersIFollow", "everyone"] 799 + } 800 + } 801 + }, 802 + "postInteractionSettingsPref": { 803 + "type": "object", 804 + "required": [], 805 + "properties": { 806 + "threadgateAllowRules": { 807 + "type": "array", 808 + "items": { 809 + "refs": [ 810 + "app.bsky.feed.threadgate#mentionRule", 811 + "app.bsky.feed.threadgate#followerRule", 812 + "app.bsky.feed.threadgate#followingRule", 813 + "app.bsky.feed.threadgate#listRule" 814 + ], 815 + "type": "union" 816 + }, 817 + "maxLength": 5, 818 + "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." 819 + }, 820 + "postgateEmbeddingRules": { 821 + "type": "array", 822 + "items": { 823 + "refs": ["app.bsky.feed.postgate#disableRule"], 824 + "type": "union" 825 + }, 826 + "maxLength": 5, 827 + "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." 828 + } 829 + }, 830 + "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." 831 + }, 832 + "profileAssociatedActivitySubscription": { 833 + "type": "object", 834 + "required": ["allowSubscriptions"], 835 + "properties": { 836 + "allowSubscriptions": { 837 + "type": "string", 838 + "knownValues": ["followers", "mutuals", "none"] 839 + } 840 + } 841 + } 842 + }, 843 + "$type": "com.atproto.lexicon.schema", 844 + "lexicon": 1 845 + }
+41
lexicons/app/bsky/actor/getProfiles.json
··· 1 + { 2 + "id": "app.bsky.actor.getProfiles", 3 + "defs": { 4 + "main": { 5 + "type": "query", 6 + "output": { 7 + "schema": { 8 + "type": "object", 9 + "required": ["profiles"], 10 + "properties": { 11 + "profiles": { 12 + "type": "array", 13 + "items": { 14 + "ref": "app.bsky.actor.defs#profileViewDetailed", 15 + "type": "ref" 16 + } 17 + } 18 + } 19 + }, 20 + "encoding": "application/json" 21 + }, 22 + "parameters": { 23 + "type": "params", 24 + "required": ["actors"], 25 + "properties": { 26 + "actors": { 27 + "type": "array", 28 + "items": { 29 + "type": "string", 30 + "format": "at-identifier" 31 + }, 32 + "maxLength": 25 33 + } 34 + } 35 + }, 36 + "description": "Get detailed profile views of multiple actors." 37 + } 38 + }, 39 + "$type": "com.atproto.lexicon.schema", 40 + "lexicon": 1 41 + }
+41
lexicons/app/bsky/actor/status.json
··· 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": ["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 + }
+22
lexicons/app/bsky/embed/defs.json
··· 1 + { 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 + }
+69
lexicons/app/bsky/embed/external.json
··· 1 + { 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 + }
+79
lexicons/app/bsky/embed/images.json
··· 1 + { 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 + }
+141
lexicons/app/bsky/embed/record.json
··· 1 + { 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 + }
+40
lexicons/app/bsky/embed/recordWithMedia.json
··· 1 + { 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 + }
+90
lexicons/app/bsky/embed/video.json
··· 1 + { 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 + "presentation": { 33 + "type": "string", 34 + "description": "A hint to the client about how to present the video.", 35 + "knownValues": ["default", "gif"] 36 + } 37 + } 38 + }, 39 + "view": { 40 + "type": "object", 41 + "required": ["cid", "playlist"], 42 + "properties": { 43 + "alt": { 44 + "type": "string", 45 + "maxLength": 10000, 46 + "maxGraphemes": 1000 47 + }, 48 + "cid": { 49 + "type": "string", 50 + "format": "cid" 51 + }, 52 + "playlist": { 53 + "type": "string", 54 + "format": "uri" 55 + }, 56 + "thumbnail": { 57 + "type": "string", 58 + "format": "uri" 59 + }, 60 + "aspectRatio": { 61 + "ref": "app.bsky.embed.defs#aspectRatio", 62 + "type": "ref" 63 + }, 64 + "presentation": { 65 + "type": "string", 66 + "description": "A hint to the client about how to present the video.", 67 + "knownValues": ["default", "gif"] 68 + } 69 + } 70 + }, 71 + "caption": { 72 + "type": "object", 73 + "required": ["lang", "file"], 74 + "properties": { 75 + "file": { 76 + "type": "blob", 77 + "accept": ["text/vtt"], 78 + "maxSize": 20000 79 + }, 80 + "lang": { 81 + "type": "string", 82 + "format": "language" 83 + } 84 + } 85 + } 86 + }, 87 + "$type": "com.atproto.lexicon.schema", 88 + "lexicon": 1, 89 + "description": "A video embedded in a Bluesky record (eg, a post)." 90 + }
+486
lexicons/app/bsky/feed/defs.json
··· 1 + { 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 + }
+81
lexicons/app/bsky/feed/getLikes.json
··· 1 + { 2 + "id": "app.bsky.feed.getLikes", 3 + "defs": { 4 + "like": { 5 + "type": "object", 6 + "required": ["indexedAt", "createdAt", "actor"], 7 + "properties": { 8 + "actor": { 9 + "ref": "app.bsky.actor.defs#profileView", 10 + "type": "ref" 11 + }, 12 + "createdAt": { 13 + "type": "string", 14 + "format": "datetime" 15 + }, 16 + "indexedAt": { 17 + "type": "string", 18 + "format": "datetime" 19 + } 20 + } 21 + }, 22 + "main": { 23 + "type": "query", 24 + "output": { 25 + "schema": { 26 + "type": "object", 27 + "required": ["uri", "likes"], 28 + "properties": { 29 + "cid": { 30 + "type": "string", 31 + "format": "cid" 32 + }, 33 + "uri": { 34 + "type": "string", 35 + "format": "at-uri" 36 + }, 37 + "likes": { 38 + "type": "array", 39 + "items": { 40 + "ref": "#like", 41 + "type": "ref" 42 + } 43 + }, 44 + "cursor": { 45 + "type": "string" 46 + } 47 + } 48 + }, 49 + "encoding": "application/json" 50 + }, 51 + "parameters": { 52 + "type": "params", 53 + "required": ["uri"], 54 + "properties": { 55 + "cid": { 56 + "type": "string", 57 + "format": "cid", 58 + "description": "CID of the subject record (aka, specific version of record), to filter likes." 59 + }, 60 + "uri": { 61 + "type": "string", 62 + "format": "at-uri", 63 + "description": "AT-URI of the subject (eg, a post record)." 64 + }, 65 + "limit": { 66 + "type": "integer", 67 + "default": 50, 68 + "maximum": 100, 69 + "minimum": 1 70 + }, 71 + "cursor": { 72 + "type": "string" 73 + } 74 + } 75 + }, 76 + "description": "Get like records which reference a subject (by AT-URI and CID)." 77 + } 78 + }, 79 + "$type": "com.atproto.lexicon.schema", 80 + "lexicon": 1 81 + }
+62
lexicons/app/bsky/feed/getPostThread.json
··· 1 + { 2 + "id": "app.bsky.feed.getPostThread", 3 + "defs": { 4 + "main": { 5 + "type": "query", 6 + "errors": [ 7 + { 8 + "name": "NotFound" 9 + } 10 + ], 11 + "output": { 12 + "schema": { 13 + "type": "object", 14 + "required": ["thread"], 15 + "properties": { 16 + "thread": { 17 + "refs": [ 18 + "app.bsky.feed.defs#threadViewPost", 19 + "app.bsky.feed.defs#notFoundPost", 20 + "app.bsky.feed.defs#blockedPost" 21 + ], 22 + "type": "union" 23 + }, 24 + "threadgate": { 25 + "ref": "app.bsky.feed.defs#threadgateView", 26 + "type": "ref" 27 + } 28 + } 29 + }, 30 + "encoding": "application/json" 31 + }, 32 + "parameters": { 33 + "type": "params", 34 + "required": ["uri"], 35 + "properties": { 36 + "uri": { 37 + "type": "string", 38 + "format": "at-uri", 39 + "description": "Reference (AT-URI) to post record." 40 + }, 41 + "depth": { 42 + "type": "integer", 43 + "default": 6, 44 + "maximum": 1000, 45 + "minimum": 0, 46 + "description": "How many levels of reply depth should be included in response." 47 + }, 48 + "parentHeight": { 49 + "type": "integer", 50 + "default": 80, 51 + "maximum": 1000, 52 + "minimum": 0, 53 + "description": "How many levels of parent (and grandparent, etc) post to include." 54 + } 55 + } 56 + }, 57 + "description": "Get posts in a thread. Does not require auth, but additional metadata and filtering will be applied for authed requests." 58 + } 59 + }, 60 + "$type": "com.atproto.lexicon.schema", 61 + "lexicon": 1 62 + }
+42
lexicons/app/bsky/feed/getPosts.json
··· 1 + { 2 + "id": "app.bsky.feed.getPosts", 3 + "defs": { 4 + "main": { 5 + "type": "query", 6 + "output": { 7 + "schema": { 8 + "type": "object", 9 + "required": ["posts"], 10 + "properties": { 11 + "posts": { 12 + "type": "array", 13 + "items": { 14 + "ref": "app.bsky.feed.defs#postView", 15 + "type": "ref" 16 + } 17 + } 18 + } 19 + }, 20 + "encoding": "application/json" 21 + }, 22 + "parameters": { 23 + "type": "params", 24 + "required": ["uris"], 25 + "properties": { 26 + "uris": { 27 + "type": "array", 28 + "items": { 29 + "type": "string", 30 + "format": "at-uri" 31 + }, 32 + "maxLength": 25, 33 + "description": "List of post AT-URIs to return hydrated views for." 34 + } 35 + } 36 + }, 37 + "description": "Gets post views for a specified list of posts (by AT-URI). This is sometimes referred to as 'hydrating' a 'feed skeleton'." 38 + } 39 + }, 40 + "$type": "com.atproto.lexicon.schema", 41 + "lexicon": 1 42 + }
+130
lexicons/app/bsky/feed/post.json
··· 1 + { 2 + "id": "app.bsky.feed.post", 3 + "defs": { 4 + "main": { 5 + "key": "tid", 6 + "type": "record", 7 + "record": { 8 + "type": "object", 9 + "required": ["text", "createdAt"], 10 + "properties": { 11 + "tags": { 12 + "type": "array", 13 + "items": { 14 + "type": "string", 15 + "maxLength": 640, 16 + "maxGraphemes": 64 17 + }, 18 + "maxLength": 8, 19 + "description": "Additional hashtags, in addition to any included in post text and facets." 20 + }, 21 + "text": { 22 + "type": "string", 23 + "maxLength": 3000, 24 + "description": "The primary post content. May be an empty string, if there are embeds.", 25 + "maxGraphemes": 300 26 + }, 27 + "embed": { 28 + "refs": [ 29 + "app.bsky.embed.images", 30 + "app.bsky.embed.video", 31 + "app.bsky.embed.external", 32 + "app.bsky.embed.record", 33 + "app.bsky.embed.recordWithMedia" 34 + ], 35 + "type": "union" 36 + }, 37 + "langs": { 38 + "type": "array", 39 + "items": { 40 + "type": "string", 41 + "format": "language" 42 + }, 43 + "maxLength": 3, 44 + "description": "Indicates human language of post primary text content." 45 + }, 46 + "reply": { 47 + "ref": "#replyRef", 48 + "type": "ref" 49 + }, 50 + "facets": { 51 + "type": "array", 52 + "items": { 53 + "ref": "app.bsky.richtext.facet", 54 + "type": "ref" 55 + }, 56 + "description": "Annotations of text (mentions, URLs, hashtags, etc)" 57 + }, 58 + "labels": { 59 + "refs": ["com.atproto.label.defs#selfLabels"], 60 + "type": "union", 61 + "description": "Self-label values for this post. Effectively content warnings." 62 + }, 63 + "entities": { 64 + "type": "array", 65 + "items": { 66 + "ref": "#entity", 67 + "type": "ref" 68 + }, 69 + "description": "DEPRECATED: replaced by app.bsky.richtext.facet." 70 + }, 71 + "createdAt": { 72 + "type": "string", 73 + "format": "datetime", 74 + "description": "Client-declared timestamp when this post was originally created." 75 + } 76 + } 77 + }, 78 + "description": "Record containing a Bluesky post." 79 + }, 80 + "entity": { 81 + "type": "object", 82 + "required": ["index", "type", "value"], 83 + "properties": { 84 + "type": { 85 + "type": "string", 86 + "description": "Expected values are 'mention' and 'link'." 87 + }, 88 + "index": { 89 + "ref": "#textSlice", 90 + "type": "ref" 91 + }, 92 + "value": { 93 + "type": "string" 94 + } 95 + }, 96 + "description": "Deprecated: use facets instead." 97 + }, 98 + "replyRef": { 99 + "type": "object", 100 + "required": ["root", "parent"], 101 + "properties": { 102 + "root": { 103 + "ref": "com.atproto.repo.strongRef", 104 + "type": "ref" 105 + }, 106 + "parent": { 107 + "ref": "com.atproto.repo.strongRef", 108 + "type": "ref" 109 + } 110 + } 111 + }, 112 + "textSlice": { 113 + "type": "object", 114 + "required": ["start", "end"], 115 + "properties": { 116 + "end": { 117 + "type": "integer", 118 + "minimum": 0 119 + }, 120 + "start": { 121 + "type": "integer", 122 + "minimum": 0 123 + } 124 + }, 125 + "description": "Deprecated. Use app.bsky.richtext instead -- A text segment. Start is inclusive, end is exclusive. Indices are for utf16-encoded strings." 126 + } 127 + }, 128 + "$type": "com.atproto.lexicon.schema", 129 + "lexicon": 1 130 + }
+50
lexicons/app/bsky/feed/postgate.json
··· 1 + { 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 + }
+71
lexicons/app/bsky/feed/threadgate.json
··· 1 + { 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 + }
+321
lexicons/app/bsky/graph/defs.json
··· 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": ["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 + "blocking": { 105 + "type": "string", 106 + "format": "at-uri", 107 + "description": "if the actor blocks this DID, this is the AT-URI of the block record" 108 + }, 109 + "blockedBy": { 110 + "type": "string", 111 + "format": "at-uri", 112 + "description": "if the actor is blocked by this DID, contains the AT-URI of the block record" 113 + }, 114 + "following": { 115 + "type": "string", 116 + "format": "at-uri", 117 + "description": "if the actor follows this DID, this is the AT-URI of the follow record" 118 + }, 119 + "followedBy": { 120 + "type": "string", 121 + "format": "at-uri", 122 + "description": "if the actor is followed by this DID, contains the AT-URI of the follow record" 123 + }, 124 + "blockedByList": { 125 + "type": "string", 126 + "format": "at-uri", 127 + "description": "if the actor is blocked by this DID via a block list, contains the AT-URI of the listblock record" 128 + }, 129 + "blockingByList": { 130 + "type": "string", 131 + "format": "at-uri", 132 + "description": "if the actor blocks this DID via a block list, this is the AT-URI of the listblock record" 133 + } 134 + }, 135 + "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)" 136 + }, 137 + "listViewBasic": { 138 + "type": "object", 139 + "required": ["uri", "cid", "name", "purpose"], 140 + "properties": { 141 + "cid": { 142 + "type": "string", 143 + "format": "cid" 144 + }, 145 + "uri": { 146 + "type": "string", 147 + "format": "at-uri" 148 + }, 149 + "name": { 150 + "type": "string", 151 + "maxLength": 64, 152 + "minLength": 1 153 + }, 154 + "avatar": { 155 + "type": "string", 156 + "format": "uri" 157 + }, 158 + "labels": { 159 + "type": "array", 160 + "items": { 161 + "ref": "com.atproto.label.defs#label", 162 + "type": "ref" 163 + } 164 + }, 165 + "viewer": { 166 + "ref": "#listViewerState", 167 + "type": "ref" 168 + }, 169 + "purpose": { 170 + "ref": "#listPurpose", 171 + "type": "ref" 172 + }, 173 + "indexedAt": { 174 + "type": "string", 175 + "format": "datetime" 176 + }, 177 + "listItemCount": { 178 + "type": "integer", 179 + "minimum": 0 180 + } 181 + } 182 + }, 183 + "notFoundActor": { 184 + "type": "object", 185 + "required": ["actor", "notFound"], 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": ["uri", "cid", "record", "creator", "indexedAt"], 217 + "properties": { 218 + "cid": { 219 + "type": "string", 220 + "format": "cid" 221 + }, 222 + "uri": { 223 + "type": "string", 224 + "format": "at-uri" 225 + }, 226 + "list": { 227 + "ref": "#listViewBasic", 228 + "type": "ref" 229 + }, 230 + "feeds": { 231 + "type": "array", 232 + "items": { 233 + "ref": "app.bsky.feed.defs#generatorView", 234 + "type": "ref" 235 + }, 236 + "maxLength": 3 237 + }, 238 + "labels": { 239 + "type": "array", 240 + "items": { 241 + "ref": "com.atproto.label.defs#label", 242 + "type": "ref" 243 + } 244 + }, 245 + "record": { 246 + "type": "unknown" 247 + }, 248 + "creator": { 249 + "ref": "app.bsky.actor.defs#profileViewBasic", 250 + "type": "ref" 251 + }, 252 + "indexedAt": { 253 + "type": "string", 254 + "format": "datetime" 255 + }, 256 + "joinedWeekCount": { 257 + "type": "integer", 258 + "minimum": 0 259 + }, 260 + "listItemsSample": { 261 + "type": "array", 262 + "items": { 263 + "ref": "#listItemView", 264 + "type": "ref" 265 + }, 266 + "maxLength": 12 267 + }, 268 + "joinedAllTimeCount": { 269 + "type": "integer", 270 + "minimum": 0 271 + } 272 + } 273 + }, 274 + "starterPackViewBasic": { 275 + "type": "object", 276 + "required": ["uri", "cid", "record", "creator", "indexedAt"], 277 + "properties": { 278 + "cid": { 279 + "type": "string", 280 + "format": "cid" 281 + }, 282 + "uri": { 283 + "type": "string", 284 + "format": "at-uri" 285 + }, 286 + "labels": { 287 + "type": "array", 288 + "items": { 289 + "ref": "com.atproto.label.defs#label", 290 + "type": "ref" 291 + } 292 + }, 293 + "record": { 294 + "type": "unknown" 295 + }, 296 + "creator": { 297 + "ref": "app.bsky.actor.defs#profileViewBasic", 298 + "type": "ref" 299 + }, 300 + "indexedAt": { 301 + "type": "string", 302 + "format": "datetime" 303 + }, 304 + "listItemCount": { 305 + "type": "integer", 306 + "minimum": 0 307 + }, 308 + "joinedWeekCount": { 309 + "type": "integer", 310 + "minimum": 0 311 + }, 312 + "joinedAllTimeCount": { 313 + "type": "integer", 314 + "minimum": 0 315 + } 316 + } 317 + } 318 + }, 319 + "$type": "com.atproto.lexicon.schema", 320 + "lexicon": 1 321 + }
+140
lexicons/app/bsky/labeler/defs.json
··· 1 + { 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 + }
+151
lexicons/app/bsky/notification/defs.json
··· 1 + { 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 + }
+74
lexicons/app/bsky/richtext/facet.json
··· 1 + { 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 + }
+92
lexicons/com/atproto/moderation/defs.json
··· 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": ["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 + }
+50
lexicons/com/bad-example/identity/resolveMiniDoc.json
··· 1 + { 2 + "id": "com.bad-example.identity.resolveMiniDoc", 3 + "defs": { 4 + "main": { 5 + "type": "query", 6 + "output": { 7 + "schema": { 8 + "type": "object", 9 + "required": ["did", "handle", "pds", "signing_key"], 10 + "properties": { 11 + "did": { 12 + "type": "string", 13 + "format": "did", 14 + "description": "DID, bi-directionally verified if a handle was provided in the query." 15 + }, 16 + "pds": { 17 + "type": "string", 18 + "format": "uri", 19 + "description": "The identity's PDS URL" 20 + }, 21 + "handle": { 22 + "type": "string", 23 + "format": "handle", 24 + "description": "The validated handle of the account or `handle.invalid` if the handle\ndid not bi-directionally match the DID document." 25 + }, 26 + "signing_key": { 27 + "type": "string", 28 + "description": "The atproto signing key publicKeyMultibase\n\nLegacy key encoding not supported. the key is returned directly; `id`,\n`type`, and `controller` are omitted." 29 + } 30 + } 31 + }, 32 + "encoding": "application/json" 33 + }, 34 + "parameters": { 35 + "type": "params", 36 + "required": ["identifier"], 37 + "properties": { 38 + "identifier": { 39 + "type": "string", 40 + "format": "at-identifier", 41 + "description": "Handle or DID to resolve" 42 + } 43 + } 44 + }, 45 + "description": "Like [com.atproto.identity.resolveIdentity](https://docs.bsky.app/docs/api/com-atproto-identity-resolve-identity) but instead of the full `didDoc` it returns an atproto-relevant subset." 46 + } 47 + }, 48 + "$type": "com.atproto.lexicon.schema", 49 + "lexicon": 1 50 + }
+212
lexicons/tools/ozone/report/defs.json
··· 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 + }
+5 -1
nuxt.config.ts
··· 165 165 ], 166 166 external: ['@deno/doc'], 167 167 }, 168 + esbuild: { 169 + options: { 170 + target: 'es2024', 171 + }, 172 + }, 168 173 rollupConfig: { 169 174 output: { 170 175 paths: { ··· 289 294 'semver', 290 295 'validate-npm-package-name', 291 296 '@atproto/lex', 292 - '@atproto/syntax', 293 297 'fast-npm-meta', 294 298 '@floating-ui/vue', 295 299 'algoliasearch/lite',
-2
package.json
··· 45 45 "start:playwright:webserver": "NODE_ENV=test pnpm preview --port 5678" 46 46 }, 47 47 "dependencies": { 48 - "@atproto/api": "^0.18.17", 49 48 "@atproto/common": "0.5.10", 50 49 "@atproto/lex": "0.0.13", 51 50 "@atproto/oauth-client-node": "^0.3.15", 52 - "@atproto/syntax": "0.4.3", 53 51 "@deno/doc": "jsr:^0.189.1", 54 52 "@floating-ui/vue": "1.1.10", 55 53 "@iconify-json/carbon": "1.2.18",
-31
pnpm-lock.yaml
··· 20 20 21 21 .: 22 22 dependencies: 23 - '@atproto/api': 24 - specifier: ^0.18.17 25 - version: 0.18.20 26 23 '@atproto/common': 27 24 specifier: 0.5.10 28 25 version: 0.5.10 ··· 32 29 '@atproto/oauth-client-node': 33 30 specifier: ^0.3.15 34 31 version: 0.3.16 35 - '@atproto/syntax': 36 - specifier: 0.4.3 37 - version: 0.4.3 38 32 '@deno/doc': 39 33 specifier: jsr:^0.189.1 40 34 version: '@jsr/deno__doc@0.189.1(patch_hash=24f326e123c822a07976329a5afe91a8713e82d53134b5586625b72431c87832)' ··· 445 439 '@atproto-labs/simple-store@0.3.0': 446 440 resolution: {integrity: sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ==} 447 441 448 - '@atproto/api@0.18.20': 449 - resolution: {integrity: sha512-BZYZkh2VJIFCXEnc/vzKwAwWjAQQTgbNJ8FBxpBK+z+KYh99O0uPCsRYKoCQsRrnkgrhzdU9+g2G+7zanTIGbw==} 450 - 451 442 '@atproto/common-web@0.4.15': 452 443 resolution: {integrity: sha512-A4l9gyqUNez8CjZp/Trypz/D3WIQsNj8dN05WR6+RoBbvwc9JhWjKPrm+WoVYc/F16RPdXHLkE3BEJlGIyYIiA==} 453 444 ··· 4762 4753 resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} 4763 4754 engines: {node: '>= 0.4'} 4764 4755 4765 - await-lock@2.2.2: 4766 - resolution: {integrity: sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==} 4767 - 4768 4756 axe-core@4.11.1: 4769 4757 resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} 4770 4758 engines: {node: '>=4'} ··· 8791 8779 tinyrainbow@3.0.3: 8792 8780 resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} 8793 8781 engines: {node: '>=14.0.0'} 8794 - 8795 - tlds@1.261.0: 8796 - resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==} 8797 - hasBin: true 8798 8782 8799 8783 to-buffer@1.2.2: 8800 8784 resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} ··· 9880 9864 9881 9865 '@atproto-labs/simple-store@0.3.0': {} 9882 9866 9883 - '@atproto/api@0.18.20': 9884 - dependencies: 9885 - '@atproto/common-web': 0.4.15 9886 - '@atproto/lexicon': 0.6.1 9887 - '@atproto/syntax': 0.4.3 9888 - '@atproto/xrpc': 0.7.7 9889 - await-lock: 2.2.2 9890 - multiformats: 9.9.0 9891 - tlds: 1.261.0 9892 - zod: 3.25.76 9893 - 9894 9867 '@atproto/common-web@0.4.15': 9895 9868 dependencies: 9896 9869 '@atproto/lex-data': 0.0.10 ··· 14872 14845 available-typed-arrays@1.0.7: 14873 14846 dependencies: 14874 14847 possible-typed-array-names: 1.1.0 14875 - 14876 - await-lock@2.2.2: {} 14877 14848 14878 14849 axe-core@4.11.1: {} 14879 14850 ··· 20312 20283 tinypool@2.0.0: {} 20313 20284 20314 20285 tinyrainbow@3.0.3: {} 20315 - 20316 - tlds@1.261.0: {} 20317 20286 20318 20287 to-buffer@1.2.2: 20319 20288 dependencies:
+7 -13
server/api/atproto/author-profiles.get.ts
··· 1 1 import * as v from 'valibot' 2 2 import { CACHE_MAX_AGE_ONE_DAY, BLUESKY_API } from '#shared/utils/constants' 3 3 import { AuthorSchema } from '#shared/schemas/blog' 4 + import { Client } from '@atproto/lex' 4 5 import type { Author, ResolvedAuthor } from '#shared/schemas/blog' 5 - 6 - type ProfilesResponse = { 7 - profiles: Array<{ 8 - did: string 9 - handle: string 10 - displayName?: string 11 - avatar?: string 12 - }> 13 - } 6 + import * as app from '#shared/types/lexicons/app' 14 7 15 8 export default defineCachedEventHandler( 16 9 async event => { ··· 45 38 return { authors: [] } 46 39 } 47 40 48 - const handles = authors.filter(a => a.blueskyHandle).map(a => a.blueskyHandle as string) 41 + const handles = authors.map(a => a.blueskyHandle).filter(v => v != null) 49 42 50 43 if (handles.length === 0) { 51 44 return { ··· 53 46 } 54 47 } 55 48 56 - const response = await $fetch<ProfilesResponse>(`${BLUESKY_API}app.bsky.actor.getProfiles`, { 57 - query: { actors: handles }, 58 - }).catch(() => ({ profiles: [] })) 49 + const client = new Client({ service: BLUESKY_API }) 50 + const response = await client 51 + .call(app.bsky.actor.getProfiles, { actors: handles }) 52 + .catch(() => ({ profiles: [] })) 59 53 60 54 const avatarMap = new Map<string, string>() 61 55 for (const profile of response.profiles) {
+30 -53
server/api/atproto/bluesky-comments.get.ts
··· 1 - import { safeParse, flatten } from 'valibot' 1 + import type { $Typed, AtUriString, Unknown$TypedObject } from '@atproto/lex' 2 + import { Client, isAtUriString } from '@atproto/lex' 2 3 import type { Comment, CommentEmbed } from '#shared/types/blog-post' 4 + import * as app from '#shared/types/lexicons/app' 3 5 import { 4 - AppBskyFeedDefs, 5 - AppBskyFeedPost, 6 - AppBskyEmbedImages, 7 - AppBskyEmbedExternal, 8 - } from '@atproto/api' 9 - import { BlueSkyUriSchema } from '#shared/schemas/atproto' 10 - import { CACHE_MAX_AGE_ONE_MINUTE, BLUESKY_API, AT_URI_REGEX } from '#shared/utils/constants' 11 - 12 - import { jsonToLex } from '@atproto/api' 13 - 14 - type ThreadResponse = { thread: AppBskyFeedDefs.ThreadViewPost } 15 - 16 - type LikesResponse = { 17 - likes: Array<{ 18 - actor: { 19 - did: string 20 - handle: string 21 - displayName?: string 22 - avatar?: string 23 - } 24 - }> 25 - } 6 + CACHE_MAX_AGE_ONE_MINUTE, 7 + BLUESKY_API, 8 + BSKY_POST_AT_URI_REGEX, 9 + } from '#shared/utils/constants' 26 10 27 - type PostsResponse = { posts: Array<{ likeCount?: number }> } 28 - 29 - const $bluesky = $fetch.create({ baseURL: BLUESKY_API }) 11 + const blueskyClient = new Client({ service: BLUESKY_API }) 30 12 31 13 /** 32 14 * Provides both build and runtime comments refreshes ··· 35 17 */ 36 18 export default defineCachedEventHandler( 37 19 async event => { 38 - const query = getQuery(event) 39 - const parsed = safeParse(BlueSkyUriSchema, query) 20 + const { uri } = getQuery(event) 40 21 41 - if (!parsed.success) { 22 + if (typeof uri !== 'string' || !isAtUriString(uri)) { 42 23 throw createError({ 43 24 statusCode: 400, 44 - statusMessage: `Invalid URI format: ${flatten(parsed.issues).root?.[0] || 'Must be a valid at:// URI'}`, 25 + statusMessage: `Invalid URI format: Must be a valid at:// URI`, 45 26 }) 46 27 } 47 28 48 - const { uri } = parsed.output 49 - 50 29 try { 51 30 // Fetch thread, likes, and post metadata in parallel 52 31 const [threadResponse, likesResponse, postsResponse] = await Promise.all([ 53 - $bluesky<ThreadResponse>('/app.bsky.feed.getPostThread', { 54 - query: { uri, depth: 10 }, 55 - }).catch((err: Error) => { 32 + blueskyClient.call(app.bsky.feed.getPostThread, { uri, depth: 10 }).catch((err: Error) => { 56 33 console.warn(`[Bluesky] Thread fetch failed for ${uri}:`, err.message) 57 34 return null 58 35 }), 59 36 60 - $bluesky<LikesResponse>('/app.bsky.feed.getLikes', { 61 - query: { uri, limit: 50 }, 62 - }).catch(() => ({ likes: [] })), 37 + blueskyClient.call(app.bsky.feed.getLikes, { uri, limit: 50 }).catch(() => ({ likes: [] })), 63 38 64 - $bluesky<PostsResponse>('/app.bsky.feed.getPosts', { 65 - query: { uris: [uri] }, 66 - }).catch(() => ({ posts: [] })), 39 + blueskyClient.call(app.bsky.feed.getPosts, { uris: [uri] }).catch(() => ({ posts: [] })), 67 40 ]) 68 41 69 42 // Early return if thread fetch fails w/o 404 ··· 108 81 ) 109 82 110 83 // Helper to convert AT URI to web URL 111 - function atUriToWebUrl(uri: string): string | null { 112 - const match = uri.match(AT_URI_REGEX) 84 + function atUriToWebUrl(uri: AtUriString): string | null { 85 + const match = uri.match(BSKY_POST_AT_URI_REGEX) 113 86 if (!match) return null 114 - const [, did, rkey] = match 87 + const [, did, rkey] = match as [string, `did:plc:${string}`, string] 115 88 return `https://bsky.app/profile/${did}/post/${rkey}` 116 89 } 117 90 118 - function parseEmbed(embed: AppBskyFeedDefs.PostView['embed']): CommentEmbed | undefined { 91 + function parseEmbed(embed: app.bsky.feed.defs.PostView['embed']): CommentEmbed | undefined { 119 92 if (!embed) return undefined 120 93 121 - if (AppBskyEmbedImages.isView(embed)) { 94 + if (app.bsky.embed.images.view.$isTypeOf(embed)) { 122 95 return { 123 96 type: 'images', 124 97 images: embed.images, 125 98 } 126 99 } 127 100 128 - if (AppBskyEmbedExternal.isView(embed)) { 101 + if (app.bsky.embed.external.view.$isTypeOf(embed)) { 129 102 return { 130 103 type: 'external', 131 104 external: embed.external, ··· 135 108 return undefined 136 109 } 137 110 138 - function parseThread(thread: AppBskyFeedDefs.ThreadViewPost): Comment | null { 139 - if (!AppBskyFeedDefs.isThreadViewPost(thread)) return null 111 + function parseThread( 112 + thread: 113 + | Unknown$TypedObject 114 + | $Typed<app.bsky.feed.defs.ThreadViewPost> 115 + | $Typed<app.bsky.feed.defs.NotFoundPost> 116 + | $Typed<app.bsky.feed.defs.BlockedPost>, 117 + ): Comment | null { 118 + if (!app.bsky.feed.defs.threadViewPost.$isTypeOf(thread)) return null 140 119 141 120 const { post } = thread 142 121 143 - // This casts our external.thumb as a blobRef which is needed to validateRecord 144 - const lexPostRecord = jsonToLex(post.record) 145 - const recordValidation = AppBskyFeedPost.validateRecord(lexPostRecord) 122 + const recordValidation = app.bsky.feed.post.$safeValidate(post.record) 146 123 147 124 if (!recordValidation.success) return null 148 125 const record = recordValidation.value ··· 150 127 const replies: Comment[] = [] 151 128 if (thread.replies) { 152 129 for (const reply of thread.replies) { 153 - if (AppBskyFeedDefs.isThreadViewPost(reply)) { 130 + if (app.bsky.feed.defs.threadViewPost.$isTypeOf(reply)) { 154 131 const parsed = parseThread(reply) 155 132 if (parsed) replies.push(parsed) 156 133 }
+14 -13
server/api/auth/atproto.get.ts
··· 1 - import { Agent } from '@atproto/api' 2 1 import { NodeOAuthClient } from '@atproto/oauth-client-node' 3 2 import { createError, getQuery, sendRedirect } from 'h3' 4 3 import { getOAuthLock } from '#server/utils/atproto/lock' 5 4 import { useOAuthStorage } from '#server/utils/atproto/storage' 6 5 import { SLINGSHOT_HOST } from '#shared/utils/constants' 7 6 import { useServerSession } from '#server/utils/server-session' 8 - import type { PublicUserSession } from '#shared/schemas/publicUserSession' 9 7 import { handleResolver } from '#server/utils/atproto/oauth' 10 8 import { Client } from '@atproto/lex' 9 + import * as com from '#shared/types/lexicons/com' 11 10 import * as app from '#shared/types/lexicons/app' 12 - import { ensureValidAtIdentifier } from '@atproto/syntax' 11 + import { isAtIdentifierString } from '@atproto/lex' 13 12 // @ts-expect-error virtual file from oauth module 14 13 import { clientUri } from '#oauth/config' 15 14 ··· 23 22 * @returns 24 23 */ 25 24 async function getAvatar(did: string, pds: string) { 25 + if (!isAtIdentifierString(did)) { 26 + return undefined 27 + } 28 + 26 29 let avatar: string | undefined 27 30 try { 28 31 const pdsUrl = new URL(pds) 29 32 // Only fetch from HTTPS PDS endpoints to prevent SSRF 30 - if (did && pdsUrl.protocol === 'https:') { 31 - ensureValidAtIdentifier(did) 33 + if (pdsUrl.protocol === 'https:') { 32 34 const client = new Client(pdsUrl) 33 35 const profileResponse = await client.get(app.bsky.actor.profile, { 34 36 repo: did, ··· 133 135 const { session: authSession } = await atclient.callback( 134 136 new URLSearchParams(query as Record<string, string>), 135 137 ) 136 - const agent = new Agent(authSession) 137 - event.context.agent = agent 138 138 139 - const response = await fetch( 140 - `https://${SLINGSHOT_HOST}/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${agent.did}`, 141 - { headers: { 'User-Agent': 'npmx' } }, 142 - ) 143 - if (response.ok) { 144 - const miniDoc: PublicUserSession = await response.json() 139 + const client = new Client({ service: `https://${SLINGSHOT_HOST}` }) 140 + const response = await client.xrpcSafe(com['bad-example'].identity.resolveMiniDoc, { 141 + headers: { 'User-Agent': 'npmx' }, 142 + params: { identifier: authSession.did }, 143 + }) 144 + if (response.success) { 145 + const miniDoc = response.body 145 146 146 147 let avatar: string | undefined = await getAvatar(authSession.did, miniDoc.pds) 147 148
-14
shared/schemas/atproto.ts
··· 1 - import { object, string, startsWith, minLength, regex, pipe } from 'valibot' 2 - import type { InferOutput } from 'valibot' 3 - import { AT_URI_REGEX } from '#shared/utils/constants' 4 - 5 - export const BlueSkyUriSchema = object({ 6 - uri: pipe( 7 - string(), 8 - startsWith('at://'), 9 - minLength(10), 10 - regex(AT_URI_REGEX, 'Must be a valid at:// URI'), 11 - ), 12 - }) 13 - 14 - export type BlueSkyUri = InferOutput<typeof BlueSkyUriSchema>
+8 -2
shared/schemas/blog.ts
··· 1 - import { object, string, optional, array, boolean, pipe, isoDate } from 'valibot' 1 + import { isAtIdentifierString, type AtIdentifierString } from '@atproto/lex' 2 + import { custom, object, string, optional, array, boolean, pipe, isoDate } from 'valibot' 2 3 import type { InferOutput } from 'valibot' 3 4 4 5 export const AuthorSchema = object({ 5 6 name: string(), 6 - blueskyHandle: optional(string()), 7 + blueskyHandle: optional( 8 + pipe( 9 + string(), 10 + custom<AtIdentifierString>(v => typeof v === 'string' && isAtIdentifierString(v)), 11 + ), 12 + ), 7 13 }) 8 14 9 15 export const BlogPostSchema = object({
+5 -10
shared/types/blog-post.ts
··· 1 - import type { 2 - AppBskyActorDefs, 3 - AppBskyRichtextFacet, 4 - AppBskyEmbedImages, 5 - AppBskyEmbedExternal, 6 - } from '@atproto/api' 1 + import type * as app from '#shared/types/lexicons/app' 7 2 8 3 export type CommentEmbed = 9 - | { type: 'images'; images: AppBskyEmbedImages.ViewImage[] } 10 - | { type: 'external'; external: AppBskyEmbedExternal.ViewExternal } 4 + | { type: 'images'; images: app.bsky.embed.images.ViewImage[] } 5 + | { type: 'external'; external: app.bsky.embed.external.ViewExternal } 11 6 12 7 export interface Comment { 13 8 uri: string 14 9 cid: string 15 - author: Pick<AppBskyActorDefs.ProfileViewBasic, 'did' | 'handle' | 'displayName' | 'avatar'> 10 + author: Pick<app.bsky.actor.defs.ProfileViewBasic, 'did' | 'handle' | 'displayName' | 'avatar'> 16 11 text: string 17 - facets?: AppBskyRichtextFacet.Main[] 12 + facets?: app.bsky.richtext.facet.Main[] 18 13 embed?: CommentEmbed 19 14 createdAt: string 20 15 likeCount: number
+3 -2
shared/utils/constants.ts
··· 9 9 10 10 // API Strings 11 11 export const NPMX_SITE = 'https://npmx.dev' 12 - export const BLUESKY_API = 'https://public.api.bsky.app/xrpc/' 12 + export const BLUESKY_API = 'https://public.api.bsky.app' 13 13 export const BLUESKY_COMMENTS_REQUEST = '/api/atproto/bluesky-comments' 14 14 export const NPM_REGISTRY = 'https://registry.npmjs.org' 15 15 export const NPM_API = 'https://api.npmjs.org' ··· 74 74 } as const 75 75 76 76 // Regex 77 - export const AT_URI_REGEX = /^at:\/\/(did:plc:[a-z0-9]+)\/app\.bsky\.feed\.post\/([a-z0-9]+)$/ 77 + export const BSKY_POST_AT_URI_REGEX = 78 + /^at:\/\/(did:plc:[a-z0-9]+)\/app\.bsky\.feed\.post\/([a-z0-9]+)$/
+9
shared/utils/constellation.ts
··· 62 62 filterByDids: [string][] = [], 63 63 ttl: number | undefined = undefined, 64 64 ) { 65 + // Note that using Client from @atproto/lex here is kinda "hard" because it 66 + // expects a native fetch implementation, which is "hard" to provide using 67 + // this.cachedFetch as underlying fetch implementation. In addition to this, 68 + // blue.microcosm.links.getBacklinks is not a published lexicon, meaning 69 + // that we cannot install it using `pnpm exec lex install 70 + // blue.microcosm.links.getBacklinks` and get generated type definitions, 71 + // which kinda defeats the purpose of using Client in the first place. For 72 + // these reasons, we are using this.cachedFetch directly to call the 73 + // constellation API endpoint, and type casting the response. 65 74 const source = encodeURIComponent(`${collection}:${recordPath}`) 66 75 let urlToCall = `https://${CONSTELLATION_HOST}/xrpc/blue.microcosm.links.getBacklinks?subject=${encodeURIComponent(subject)}&source=${source}&limit=${limit}` 67 76 if (cursor) urlToCall += `&cursor=${cursor}`