Your music, beautifully tracked. All yours. (coming soon) teal.fm
teal-fm atproto

Add lexicons (will remove on move to teal monorepo?)

+1241 -19
+149
lexicons/server/index.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { 5 + createServer as createXrpcServer, 6 + Server as XrpcServer, 7 + Options as XrpcOptions, 8 + AuthVerifier, 9 + StreamAuthVerifier, 10 + } from '@atproto/xrpc-server' 11 + import { schemas } from './lexicons' 12 + 13 + export function createServer(options?: XrpcOptions): Server { 14 + return new Server(options) 15 + } 16 + 17 + export class Server { 18 + xrpc: XrpcServer 19 + app: AppNS 20 + fm: FmNS 21 + xyz: XyzNS 22 + 23 + constructor(options?: XrpcOptions) { 24 + this.xrpc = createXrpcServer(schemas, options) 25 + this.app = new AppNS(this) 26 + this.fm = new FmNS(this) 27 + this.xyz = new XyzNS(this) 28 + } 29 + } 30 + 31 + export class AppNS { 32 + _server: Server 33 + bsky: AppBskyNS 34 + 35 + constructor(server: Server) { 36 + this._server = server 37 + this.bsky = new AppBskyNS(server) 38 + } 39 + } 40 + 41 + export class AppBskyNS { 42 + _server: Server 43 + actor: AppBskyActorNS 44 + richtext: AppBskyRichtextNS 45 + 46 + constructor(server: Server) { 47 + this._server = server 48 + this.actor = new AppBskyActorNS(server) 49 + this.richtext = new AppBskyRichtextNS(server) 50 + } 51 + } 52 + 53 + export class AppBskyActorNS { 54 + _server: Server 55 + 56 + constructor(server: Server) { 57 + this._server = server 58 + } 59 + } 60 + 61 + export class AppBskyRichtextNS { 62 + _server: Server 63 + 64 + constructor(server: Server) { 65 + this._server = server 66 + } 67 + } 68 + 69 + export class FmNS { 70 + _server: Server 71 + teal: FmTealNS 72 + 73 + constructor(server: Server) { 74 + this._server = server 75 + this.teal = new FmTealNS(server) 76 + } 77 + } 78 + 79 + export class FmTealNS { 80 + _server: Server 81 + alpha: FmTealAlphaNS 82 + 83 + constructor(server: Server) { 84 + this._server = server 85 + this.alpha = new FmTealAlphaNS(server) 86 + } 87 + } 88 + 89 + export class FmTealAlphaNS { 90 + _server: Server 91 + actor: FmTealAlphaActorNS 92 + 93 + constructor(server: Server) { 94 + this._server = server 95 + this.actor = new FmTealAlphaActorNS(server) 96 + } 97 + } 98 + 99 + export class FmTealAlphaActorNS { 100 + _server: Server 101 + 102 + constructor(server: Server) { 103 + this._server = server 104 + } 105 + } 106 + 107 + export class XyzNS { 108 + _server: Server 109 + statusphere: XyzStatusphereNS 110 + 111 + constructor(server: Server) { 112 + this._server = server 113 + this.statusphere = new XyzStatusphereNS(server) 114 + } 115 + } 116 + 117 + export class XyzStatusphereNS { 118 + _server: Server 119 + 120 + constructor(server: Server) { 121 + this._server = server 122 + } 123 + } 124 + 125 + type SharedRateLimitOpts<T> = { 126 + name: string 127 + calcKey?: (ctx: T) => string 128 + calcPoints?: (ctx: T) => number 129 + } 130 + type RouteRateLimitOpts<T> = { 131 + durationMs: number 132 + points: number 133 + calcKey?: (ctx: T) => string 134 + calcPoints?: (ctx: T) => number 135 + } 136 + type HandlerOpts = { blobLimit?: number } 137 + type HandlerRateLimitOpts<T> = SharedRateLimitOpts<T> | RouteRateLimitOpts<T> 138 + type ConfigOf<Auth, Handler, ReqCtx> = 139 + | Handler 140 + | { 141 + auth?: Auth 142 + opts?: HandlerOpts 143 + rateLimit?: HandlerRateLimitOpts<ReqCtx> | HandlerRateLimitOpts<ReqCtx>[] 144 + handler: Handler 145 + } 146 + type ExtractAuth<AV extends AuthVerifier | StreamAuthVerifier> = Extract< 147 + Awaited<ReturnType<AV>>, 148 + { credentials: unknown } 149 + >
+368
lexicons/server/lexicons.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { LexiconDoc, Lexicons } from '@atproto/lexicon' 5 + 6 + export const schemaDict = { 7 + AppBskyActorProfile: { 8 + lexicon: 1, 9 + id: 'app.bsky.actor.profile', 10 + defs: { 11 + main: { 12 + type: 'record', 13 + description: 'A declaration of a Bluesky account profile.', 14 + key: 'literal:self', 15 + record: { 16 + type: 'object', 17 + properties: { 18 + displayName: { 19 + type: 'string', 20 + maxGraphemes: 64, 21 + maxLength: 640, 22 + }, 23 + description: { 24 + type: 'string', 25 + description: 'Free-form profile description text.', 26 + maxGraphemes: 256, 27 + maxLength: 2560, 28 + }, 29 + avatar: { 30 + type: 'blob', 31 + description: 32 + "Small image to be displayed next to posts from account. AKA, 'profile picture'", 33 + accept: ['image/png', 'image/jpeg'], 34 + maxSize: 1000000, 35 + }, 36 + banner: { 37 + type: 'blob', 38 + description: 39 + 'Larger horizontal image to display behind profile view.', 40 + accept: ['image/png', 'image/jpeg'], 41 + maxSize: 1000000, 42 + }, 43 + labels: { 44 + type: 'union', 45 + description: 46 + 'Self-label values, specific to the Bluesky application, on the overall account.', 47 + refs: ['lex:com.atproto.label.defs#selfLabels'], 48 + }, 49 + joinedViaStarterPack: { 50 + type: 'ref', 51 + ref: 'lex:com.atproto.repo.strongRef', 52 + }, 53 + createdAt: { 54 + type: 'string', 55 + format: 'datetime', 56 + }, 57 + }, 58 + }, 59 + }, 60 + }, 61 + }, 62 + AppBskyRichtextFacet: { 63 + lexicon: 1, 64 + id: 'app.bsky.richtext.facet', 65 + defs: { 66 + main: { 67 + type: 'object', 68 + description: 'Annotation of a sub-string within rich text.', 69 + required: ['index', 'features'], 70 + properties: { 71 + index: { 72 + type: 'ref', 73 + ref: 'lex:app.bsky.richtext.facet#byteSlice', 74 + }, 75 + features: { 76 + type: 'array', 77 + items: { 78 + type: 'union', 79 + refs: [ 80 + 'lex:app.bsky.richtext.facet#mention', 81 + 'lex:app.bsky.richtext.facet#link', 82 + 'lex:app.bsky.richtext.facet#tag', 83 + ], 84 + }, 85 + }, 86 + }, 87 + }, 88 + mention: { 89 + type: 'object', 90 + description: 91 + "Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID.", 92 + required: ['did'], 93 + properties: { 94 + did: { 95 + type: 'string', 96 + format: 'did', 97 + }, 98 + }, 99 + }, 100 + link: { 101 + type: 'object', 102 + description: 103 + 'Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.', 104 + required: ['uri'], 105 + properties: { 106 + uri: { 107 + type: 'string', 108 + format: 'uri', 109 + }, 110 + }, 111 + }, 112 + tag: { 113 + type: 'object', 114 + description: 115 + "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').", 116 + required: ['tag'], 117 + properties: { 118 + tag: { 119 + type: 'string', 120 + maxLength: 640, 121 + maxGraphemes: 64, 122 + }, 123 + }, 124 + }, 125 + byteSlice: { 126 + type: 'object', 127 + description: 128 + '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.', 129 + required: ['byteStart', 'byteEnd'], 130 + properties: { 131 + byteStart: { 132 + type: 'integer', 133 + minimum: 0, 134 + }, 135 + byteEnd: { 136 + type: 'integer', 137 + minimum: 0, 138 + }, 139 + }, 140 + }, 141 + }, 142 + }, 143 + FmTealAlphaActorProfile: { 144 + lexicon: 1, 145 + id: 'fm.teal.alpha.actor.profile', 146 + defs: { 147 + main: { 148 + type: 'record', 149 + description: 150 + 'This lexicon is in a not officially released state. It is subject to change. | A declaration of a teal.fm account profile.', 151 + key: 'literal:self', 152 + record: { 153 + type: 'object', 154 + properties: { 155 + displayName: { 156 + type: 'string', 157 + maxGraphemes: 64, 158 + maxLength: 640, 159 + }, 160 + description: { 161 + type: 'string', 162 + description: 'Free-form profile description text.', 163 + maxGraphemes: 256, 164 + maxLength: 2560, 165 + }, 166 + descriptionFacets: { 167 + type: 'array', 168 + description: 169 + 'Annotations of text in the profile description (mentions, URLs, hashtags, etc).', 170 + items: { 171 + type: 'ref', 172 + ref: 'lex:app.bsky.richtext.facet', 173 + }, 174 + }, 175 + featuredItem: { 176 + type: 'ref', 177 + description: 178 + "The user's most recent item featured on their profile.", 179 + ref: 'lex:fm.teal.alpha.actor.profile#featuredItem', 180 + }, 181 + avatar: { 182 + type: 'blob', 183 + description: 184 + "Small image to be displayed next to posts from account. AKA, 'profile picture'", 185 + accept: ['image/png', 'image/jpeg'], 186 + maxSize: 1000000, 187 + }, 188 + banner: { 189 + type: 'blob', 190 + description: 191 + 'Larger horizontal image to display behind profile view.', 192 + accept: ['image/png', 'image/jpeg'], 193 + maxSize: 1000000, 194 + }, 195 + createdAt: { 196 + type: 'string', 197 + format: 'datetime', 198 + }, 199 + }, 200 + }, 201 + }, 202 + featuredItem: { 203 + type: 'object', 204 + required: ['mbid', 'type'], 205 + properties: { 206 + mbid: { 207 + type: 'string', 208 + description: 'The Musicbrainz ID of the item', 209 + }, 210 + type: { 211 + type: 'string', 212 + description: 213 + 'The type of the item. Must be a valid Musicbrainz type, e.g. album, track, recording, etc.', 214 + }, 215 + }, 216 + }, 217 + }, 218 + }, 219 + FmTealAlphaActorStatus: { 220 + lexicon: 1, 221 + id: 'fm.teal.alpha.actor.status', 222 + defs: { 223 + main: { 224 + type: 'record', 225 + description: 226 + 'This lexicon is in a not officially released state. It is subject to change. | A declaration of the status of the actor. Only one can be shown at a time. If there are multiple, the latest record should be picked and earlier records should be deleted or tombstoned.', 227 + key: 'literal:self', 228 + record: { 229 + type: 'object', 230 + required: ['time', 'item'], 231 + properties: { 232 + time: { 233 + type: 'string', 234 + format: 'datetime', 235 + description: 'The unix timestamp of when the item was recorded', 236 + }, 237 + item: { 238 + type: 'union', 239 + refs: ['lex:fm.teal.alpha.play#record'], 240 + }, 241 + }, 242 + }, 243 + }, 244 + }, 245 + }, 246 + FmTealAlphaPlay: { 247 + lexicon: 1, 248 + id: 'fm.teal.alpha.play', 249 + description: 250 + "This lexicon is in a not officially released state. It is subject to change. | A declaration of a teal.fm play. Plays are submitted as a result of a user listening to a track. Plays should be marked as tracked when a user has listened to the entire track if it's under 2 minutes long, or half of the track's duration up to 4 minutes, whichever is longest.", 251 + defs: { 252 + main: { 253 + type: 'record', 254 + key: 'tid', 255 + record: { 256 + type: 'object', 257 + required: ['trackName', 'artistName'], 258 + properties: { 259 + trackName: { 260 + type: 'string', 261 + minLength: 1, 262 + maxLength: 256, 263 + maxGraphemes: 2560, 264 + description: 'The name of the track', 265 + }, 266 + trackMbId: { 267 + type: 'string', 268 + description: 'The Musicbrainz ID of the track', 269 + }, 270 + recordingMbId: { 271 + type: 'string', 272 + description: 'The Musicbrainz recording ID of the track', 273 + }, 274 + duration: { 275 + type: 'integer', 276 + description: 'The length of the track in seconds', 277 + }, 278 + artistName: { 279 + type: 'string', 280 + minLength: 1, 281 + maxLength: 256, 282 + maxGraphemes: 2560, 283 + description: 'The name of the artist', 284 + }, 285 + artistMbIds: { 286 + type: 'array', 287 + items: { 288 + type: 'string', 289 + }, 290 + description: 'Array of Musicbrainz artist IDs', 291 + }, 292 + releaseName: { 293 + type: 'string', 294 + maxLength: 256, 295 + maxGraphemes: 2560, 296 + description: 'The name of the release/album', 297 + }, 298 + releaseMbId: { 299 + type: 'string', 300 + description: 'The Musicbrainz release ID', 301 + }, 302 + isrc: { 303 + type: 'string', 304 + description: 'The ISRC code associated with the recording', 305 + }, 306 + originUrl: { 307 + type: 'string', 308 + description: 'The URL associated with this track', 309 + }, 310 + musicServiceBaseDomain: { 311 + type: 'string', 312 + description: 313 + 'The base domain of the music service. e.g. music.apple.com, tidal.com, spotify.com.', 314 + }, 315 + submissionClientAgent: { 316 + type: 'string', 317 + maxLength: 256, 318 + maxGraphemes: 2560, 319 + description: 320 + 'A user-agent style string specifying the user agent. e.g. tealtracker/0.0.1b', 321 + }, 322 + playedTime: { 323 + type: 'string', 324 + format: 'datetime', 325 + description: 'The unix timestamp of when the track was played', 326 + }, 327 + }, 328 + }, 329 + }, 330 + }, 331 + }, 332 + XyzStatusphereStatus: { 333 + lexicon: 1, 334 + id: 'xyz.statusphere.status', 335 + defs: { 336 + main: { 337 + type: 'record', 338 + key: 'tid', 339 + record: { 340 + type: 'object', 341 + required: ['status', 'createdAt'], 342 + properties: { 343 + status: { 344 + type: 'string', 345 + minLength: 1, 346 + maxGraphemes: 1, 347 + maxLength: 32, 348 + }, 349 + createdAt: { 350 + type: 'string', 351 + format: 'datetime', 352 + }, 353 + }, 354 + }, 355 + }, 356 + }, 357 + }, 358 + } 359 + export const schemas: LexiconDoc[] = Object.values(schemaDict) as LexiconDoc[] 360 + export const lexicons: Lexicons = new Lexicons(schemas) 361 + export const ids = { 362 + AppBskyActorProfile: 'app.bsky.actor.profile', 363 + AppBskyRichtextFacet: 'app.bsky.richtext.facet', 364 + FmTealAlphaActorProfile: 'fm.teal.alpha.actor.profile', 365 + FmTealAlphaActorStatus: 'fm.teal.alpha.actor.status', 366 + FmTealAlphaPlay: 'fm.teal.alpha.play', 367 + XyzStatusphereStatus: 'xyz.statusphere.status', 368 + }
+38
lexicons/server/types/app/bsky/actor/profile.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { lexicons } from '../../../../lexicons' 6 + import { isObj, hasProp } from '../../../../util' 7 + import { CID } from 'multiformats/cid' 8 + import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' 9 + import * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' 10 + 11 + export interface Record { 12 + displayName?: string 13 + /** Free-form profile description text. */ 14 + description?: string 15 + /** Small image to be displayed next to posts from account. AKA, 'profile picture' */ 16 + avatar?: BlobRef 17 + /** Larger horizontal image to display behind profile view. */ 18 + banner?: BlobRef 19 + labels?: 20 + | ComAtprotoLabelDefs.SelfLabels 21 + | { $type: string; [k: string]: unknown } 22 + joinedViaStarterPack?: ComAtprotoRepoStrongRef.Main 23 + createdAt?: string 24 + [k: string]: unknown 25 + } 26 + 27 + export function isRecord(v: unknown): v is Record { 28 + return ( 29 + isObj(v) && 30 + hasProp(v, '$type') && 31 + (v.$type === 'app.bsky.actor.profile#main' || 32 + v.$type === 'app.bsky.actor.profile') 33 + ) 34 + } 35 + 36 + export function validateRecord(v: unknown): ValidationResult { 37 + return lexicons.validate('app.bsky.actor.profile#main', v) 38 + }
+98
lexicons/server/types/app/bsky/richtext/facet.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { lexicons } from '../../../../lexicons' 6 + import { isObj, hasProp } from '../../../../util' 7 + import { CID } from 'multiformats/cid' 8 + 9 + /** Annotation of a sub-string within rich text. */ 10 + export interface Main { 11 + index: ByteSlice 12 + features: (Mention | Link | Tag | { $type: string; [k: string]: unknown })[] 13 + [k: string]: unknown 14 + } 15 + 16 + export function isMain(v: unknown): v is Main { 17 + return ( 18 + isObj(v) && 19 + hasProp(v, '$type') && 20 + (v.$type === 'app.bsky.richtext.facet#main' || 21 + v.$type === 'app.bsky.richtext.facet') 22 + ) 23 + } 24 + 25 + export function validateMain(v: unknown): ValidationResult { 26 + return lexicons.validate('app.bsky.richtext.facet#main', v) 27 + } 28 + 29 + /** Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID. */ 30 + export interface Mention { 31 + did: string 32 + [k: string]: unknown 33 + } 34 + 35 + export function isMention(v: unknown): v is Mention { 36 + return ( 37 + isObj(v) && 38 + hasProp(v, '$type') && 39 + v.$type === 'app.bsky.richtext.facet#mention' 40 + ) 41 + } 42 + 43 + export function validateMention(v: unknown): ValidationResult { 44 + return lexicons.validate('app.bsky.richtext.facet#mention', v) 45 + } 46 + 47 + /** Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL. */ 48 + export interface Link { 49 + uri: string 50 + [k: string]: unknown 51 + } 52 + 53 + export function isLink(v: unknown): v is Link { 54 + return ( 55 + isObj(v) && 56 + hasProp(v, '$type') && 57 + v.$type === 'app.bsky.richtext.facet#link' 58 + ) 59 + } 60 + 61 + export function validateLink(v: unknown): ValidationResult { 62 + return lexicons.validate('app.bsky.richtext.facet#link', v) 63 + } 64 + 65 + /** 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'). */ 66 + export interface Tag { 67 + tag: string 68 + [k: string]: unknown 69 + } 70 + 71 + export function isTag(v: unknown): v is Tag { 72 + return ( 73 + isObj(v) && hasProp(v, '$type') && v.$type === 'app.bsky.richtext.facet#tag' 74 + ) 75 + } 76 + 77 + export function validateTag(v: unknown): ValidationResult { 78 + return lexicons.validate('app.bsky.richtext.facet#tag', v) 79 + } 80 + 81 + /** 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. */ 82 + export interface ByteSlice { 83 + byteStart: number 84 + byteEnd: number 85 + [k: string]: unknown 86 + } 87 + 88 + export function isByteSlice(v: unknown): v is ByteSlice { 89 + return ( 90 + isObj(v) && 91 + hasProp(v, '$type') && 92 + v.$type === 'app.bsky.richtext.facet#byteSlice' 93 + ) 94 + } 95 + 96 + export function validateByteSlice(v: unknown): ValidationResult { 97 + return lexicons.validate('app.bsky.richtext.facet#byteSlice', v) 98 + }
+56
lexicons/server/types/fm/teal/alpha/actor/profile.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { lexicons } from '../../../../../lexicons' 6 + import { isObj, hasProp } from '../../../../../util' 7 + import { CID } from 'multiformats/cid' 8 + import * as AppBskyRichtextFacet from '../../../../app/bsky/richtext/facet' 9 + 10 + export interface Record { 11 + displayName?: string 12 + /** Free-form profile description text. */ 13 + description?: string 14 + /** Annotations of text in the profile description (mentions, URLs, hashtags, etc). */ 15 + descriptionFacets?: AppBskyRichtextFacet.Main[] 16 + featuredItem?: FeaturedItem 17 + /** Small image to be displayed next to posts from account. AKA, 'profile picture' */ 18 + avatar?: BlobRef 19 + /** Larger horizontal image to display behind profile view. */ 20 + banner?: BlobRef 21 + createdAt?: string 22 + [k: string]: unknown 23 + } 24 + 25 + export function isRecord(v: unknown): v is Record { 26 + return ( 27 + isObj(v) && 28 + hasProp(v, '$type') && 29 + (v.$type === 'fm.teal.alpha.actor.profile#main' || 30 + v.$type === 'fm.teal.alpha.actor.profile') 31 + ) 32 + } 33 + 34 + export function validateRecord(v: unknown): ValidationResult { 35 + return lexicons.validate('fm.teal.alpha.actor.profile#main', v) 36 + } 37 + 38 + export interface FeaturedItem { 39 + /** The Musicbrainz ID of the item */ 40 + mbid: string 41 + /** The type of the item. Must be a valid Musicbrainz type, e.g. album, track, recording, etc. */ 42 + type: string 43 + [k: string]: unknown 44 + } 45 + 46 + export function isFeaturedItem(v: unknown): v is FeaturedItem { 47 + return ( 48 + isObj(v) && 49 + hasProp(v, '$type') && 50 + v.$type === 'fm.teal.alpha.actor.profile#featuredItem' 51 + ) 52 + } 53 + 54 + export function validateFeaturedItem(v: unknown): ValidationResult { 55 + return lexicons.validate('fm.teal.alpha.actor.profile#featuredItem', v) 56 + }
+28
lexicons/server/types/fm/teal/alpha/actor/status.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { lexicons } from '../../../../../lexicons' 6 + import { isObj, hasProp } from '../../../../../util' 7 + import { CID } from 'multiformats/cid' 8 + import * as FmTealAlphaPlay from '../play' 9 + 10 + export interface Record { 11 + /** The unix timestamp of when the item was recorded */ 12 + time: string 13 + item: FmTealAlphaPlay.Record | { $type: string; [k: string]: unknown } 14 + [k: string]: unknown 15 + } 16 + 17 + export function isRecord(v: unknown): v is Record { 18 + return ( 19 + isObj(v) && 20 + hasProp(v, '$type') && 21 + (v.$type === 'fm.teal.alpha.actor.status#main' || 22 + v.$type === 'fm.teal.alpha.actor.status') 23 + ) 24 + } 25 + 26 + export function validateRecord(v: unknown): ValidationResult { 27 + return lexicons.validate('fm.teal.alpha.actor.status#main', v) 28 + }
+49
lexicons/server/types/fm/teal/alpha/play.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { lexicons } from '../../../../lexicons' 6 + import { isObj, hasProp } from '../../../../util' 7 + import { CID } from 'multiformats/cid' 8 + 9 + export interface Record { 10 + /** The name of the track */ 11 + trackName: string 12 + /** The Musicbrainz ID of the track */ 13 + trackMbId?: string 14 + /** The Musicbrainz recording ID of the track */ 15 + recordingMbId?: string 16 + /** The length of the track in seconds */ 17 + duration?: number 18 + /** The name of the artist */ 19 + artistName: string 20 + /** Array of Musicbrainz artist IDs */ 21 + artistMbIds?: string[] 22 + /** The name of the release/album */ 23 + releaseName?: string 24 + /** The Musicbrainz release ID */ 25 + releaseMbId?: string 26 + /** The ISRC code associated with the recording */ 27 + isrc?: string 28 + /** The URL associated with this track */ 29 + originUrl?: string 30 + /** The base domain of the music service. e.g. music.apple.com, tidal.com, spotify.com. */ 31 + musicServiceBaseDomain?: string 32 + /** A user-agent style string specifying the user agent. e.g. tealtracker/0.0.1b */ 33 + submissionClientAgent?: string 34 + /** The unix timestamp of when the track was played */ 35 + playedTime?: string 36 + [k: string]: unknown 37 + } 38 + 39 + export function isRecord(v: unknown): v is Record { 40 + return ( 41 + isObj(v) && 42 + hasProp(v, '$type') && 43 + (v.$type === 'fm.teal.alpha.play#main' || v.$type === 'fm.teal.alpha.play') 44 + ) 45 + } 46 + 47 + export function validateRecord(v: unknown): ValidationResult { 48 + return lexicons.validate('fm.teal.alpha.play#main', v) 49 + }
+26
lexicons/server/types/xyz/statusphere/status.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { lexicons } from '../../../lexicons' 6 + import { isObj, hasProp } from '../../../util' 7 + import { CID } from 'multiformats/cid' 8 + 9 + export interface Record { 10 + status: string 11 + createdAt: string 12 + [k: string]: unknown 13 + } 14 + 15 + export function isRecord(v: unknown): v is Record { 16 + return ( 17 + isObj(v) && 18 + hasProp(v, '$type') && 19 + (v.$type === 'xyz.statusphere.status#main' || 20 + v.$type === 'xyz.statusphere.status') 21 + ) 22 + } 23 + 24 + export function validateRecord(v: unknown): ValidationResult { 25 + return lexicons.validate('xyz.statusphere.status#main', v) 26 + }
+13
lexicons/server/util.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + export function isObj(v: unknown): v is Record<string, unknown> { 5 + return typeof v === 'object' && v !== null 6 + } 7 + 8 + export function hasProp<K extends PropertyKey>( 9 + data: object, 10 + prop: K, 11 + ): data is Record<K, unknown> { 12 + return prop in data 13 + }
+49
lexicons/src/app.bsky.actor.profile.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.bsky.actor.profile", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "A declaration of a Bluesky account profile.", 8 + "key": "literal:self", 9 + "record": { 10 + "type": "object", 11 + "properties": { 12 + "displayName": { 13 + "type": "string", 14 + "maxGraphemes": 64, 15 + "maxLength": 640 16 + }, 17 + "description": { 18 + "type": "string", 19 + "description": "Free-form profile description text.", 20 + "maxGraphemes": 256, 21 + "maxLength": 2560 22 + }, 23 + "avatar": { 24 + "type": "blob", 25 + "description": "Small image to be displayed next to posts from account. AKA, 'profile picture'", 26 + "accept": ["image/png", "image/jpeg"], 27 + "maxSize": 1000000 28 + }, 29 + "banner": { 30 + "type": "blob", 31 + "description": "Larger horizontal image to display behind profile view.", 32 + "accept": ["image/png", "image/jpeg"], 33 + "maxSize": 1000000 34 + }, 35 + "labels": { 36 + "type": "union", 37 + "description": "Self-label values, specific to the Bluesky application, on the overall account.", 38 + "refs": ["com.atproto.label.defs#selfLabels"] 39 + }, 40 + "joinedViaStarterPack": { 41 + "type": "ref", 42 + "ref": "com.atproto.repo.strongRef" 43 + }, 44 + "createdAt": { "type": "string", "format": "datetime" } 45 + } 46 + } 47 + } 48 + } 49 + }
+51
lexicons/src/app.bsky.richtext.facet.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.bsky.richtext.facet", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "description": "Annotation of a sub-string within rich text.", 8 + "required": ["index", "features"], 9 + "properties": { 10 + "index": { "type": "ref", "ref": "#byteSlice" }, 11 + "features": { 12 + "type": "array", 13 + "items": { "type": "union", "refs": ["#mention", "#link", "#tag"] } 14 + } 15 + } 16 + }, 17 + "mention": { 18 + "type": "object", 19 + "description": "Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID.", 20 + "required": ["did"], 21 + "properties": { 22 + "did": { "type": "string", "format": "did" } 23 + } 24 + }, 25 + "link": { 26 + "type": "object", 27 + "description": "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.", 28 + "required": ["uri"], 29 + "properties": { 30 + "uri": { "type": "string", "format": "uri" } 31 + } 32 + }, 33 + "tag": { 34 + "type": "object", 35 + "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').", 36 + "required": ["tag"], 37 + "properties": { 38 + "tag": { "type": "string", "maxLength": 640, "maxGraphemes": 64 } 39 + } 40 + }, 41 + "byteSlice": { 42 + "type": "object", 43 + "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.", 44 + "required": ["byteStart", "byteEnd"], 45 + "properties": { 46 + "byteStart": { "type": "integer", "minimum": 0 }, 47 + "byteEnd": { "type": "integer", "minimum": 0 } 48 + } 49 + } 50 + } 51 + }
+64
lexicons/src/fm.teal.alpha.actor.profile.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "fm.teal.alpha.actor.profile", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "This lexicon is in a not officially released state. It is subject to change. | A declaration of a teal.fm account profile.", 8 + "key": "literal:self", 9 + "record": { 10 + "type": "object", 11 + "properties": { 12 + "displayName": { 13 + "type": "string", 14 + "maxGraphemes": 64, 15 + "maxLength": 640 16 + }, 17 + "description": { 18 + "type": "string", 19 + "description": "Free-form profile description text.", 20 + "maxGraphemes": 256, 21 + "maxLength": 2560 22 + }, 23 + "descriptionFacets": { 24 + "type": "array", 25 + "description": "Annotations of text in the profile description (mentions, URLs, hashtags, etc).", 26 + "items": { "type": "ref", "ref": "app.bsky.richtext.facet" } 27 + }, 28 + "featuredItem": { 29 + "type": "ref", 30 + "description": "The user's most recent item featured on their profile.", 31 + "ref": "#featuredItem" 32 + }, 33 + "avatar": { 34 + "type": "blob", 35 + "description": "Small image to be displayed next to posts from account. AKA, 'profile picture'", 36 + "accept": ["image/png", "image/jpeg"], 37 + "maxSize": 1000000 38 + }, 39 + "banner": { 40 + "type": "blob", 41 + "description": "Larger horizontal image to display behind profile view.", 42 + "accept": ["image/png", "image/jpeg"], 43 + "maxSize": 1000000 44 + }, 45 + "createdAt": { "type": "string", "format": "datetime" } 46 + } 47 + } 48 + }, 49 + "featuredItem": { 50 + "type": "object", 51 + "required": ["mbid", "type"], 52 + "properties": { 53 + "mbid": { 54 + "type": "string", 55 + "description": "The Musicbrainz ID of the item" 56 + }, 57 + "type": { 58 + "type": "string", 59 + "description": "The type of the item. Must be a valid Musicbrainz type, e.g. album, track, recording, etc." 60 + } 61 + } 62 + } 63 + } 64 + }
+23
lexicons/src/fm.teal.alpha.actor.status.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "fm.teal.alpha.actor.status", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "This lexicon is in a not officially released state. It is subject to change. | A declaration of the status of the actor. Only one can be shown at a time. If there are multiple, the latest record should be picked and earlier records should be deleted or tombstoned.", 8 + "key": "literal:self", 9 + "record": { 10 + "type": "object", 11 + "required": ["time", "item"], 12 + "properties": { 13 + "time": { 14 + "type": "string", 15 + "format": "datetime", 16 + "description": "The unix timestamp of when the item was recorded" 17 + }, 18 + "item": { "type": "union", "refs": ["fm.teal.alpha.play#record"] } 19 + } 20 + } 21 + } 22 + } 23 + }
+84
lexicons/src/fm.teal.alpha.play.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "fm.teal.alpha.play", 4 + "description": "This lexicon is in a not officially released state. It is subject to change. | A declaration of a teal.fm play. Plays are submitted as a result of a user listening to a track. Plays should be marked as tracked when a user has listened to the entire track if it's under 2 minutes long, or half of the track's duration up to 4 minutes, whichever is longest.", 5 + "defs": { 6 + "main": { 7 + "type": "record", 8 + "key": "tid", 9 + "record": { 10 + "type": "object", 11 + "required": ["trackName", "artistName"], 12 + "properties": { 13 + "trackName": { 14 + "type": "string", 15 + "minLength": 1, 16 + "maxLength": 256, 17 + "maxGraphemes": 2560, 18 + "description": "The name of the track" 19 + }, 20 + "trackMbId": { 21 + "type": "string", 22 + 23 + "description": "The Musicbrainz ID of the track" 24 + }, 25 + "recordingMbId": { 26 + "type": "string", 27 + "description": "The Musicbrainz recording ID of the track" 28 + }, 29 + "duration": { 30 + "type": "integer", 31 + "description": "The length of the track in seconds" 32 + }, 33 + "artistName": { 34 + "type": "string", 35 + "minLength": 1, 36 + "maxLength": 256, 37 + "maxGraphemes": 2560, 38 + "description": "The name of the artist" 39 + }, 40 + "artistMbIds": { 41 + "type": "array", 42 + "items": { 43 + "type": "string" 44 + }, 45 + "description": "Array of Musicbrainz artist IDs" 46 + }, 47 + "releaseName": { 48 + "type": "string", 49 + "maxLength": 256, 50 + "maxGraphemes": 2560, 51 + "description": "The name of the release/album" 52 + }, 53 + "releaseMbId": { 54 + "type": "string", 55 + "description": "The Musicbrainz release ID" 56 + }, 57 + "isrc": { 58 + "type": "string", 59 + "description": "The ISRC code associated with the recording" 60 + }, 61 + "originUrl": { 62 + "type": "string", 63 + "description": "The URL associated with this track" 64 + }, 65 + "musicServiceBaseDomain": { 66 + "type": "string", 67 + "description": "The base domain of the music service. e.g. music.apple.com, tidal.com, spotify.com." 68 + }, 69 + "submissionClientAgent": { 70 + "type": "string", 71 + "maxLength": 256, 72 + "maxGraphemes": 2560, 73 + "description": "A user-agent style string specifying the user agent. e.g. tealtracker/0.0.1b" 74 + }, 75 + "playedTime": { 76 + "type": "string", 77 + "format": "datetime", 78 + "description": "The unix timestamp of when the track was played" 79 + } 80 + } 81 + } 82 + } 83 + } 84 + }
+23
lexicons/src/xyz.statusphere.status.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "xyz.statusphere.status", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "record": { 9 + "type": "object", 10 + "required": ["status", "createdAt"], 11 + "properties": { 12 + "status": { 13 + "type": "string", 14 + "minLength": 1, 15 + "maxGraphemes": 1, 16 + "maxLength": 32 17 + }, 18 + "createdAt": { "type": "string", "format": "datetime" } 19 + } 20 + } 21 + } 22 + } 23 + }
+3 -1
package.json
··· 7 7 "android": "expo run:android", 8 8 "ios": "expo run:ios", 9 9 "web": "expo start --web", 10 - "test": "jest --watchAll" 10 + "test": "jest --watchAll", 11 + "lexgen": "lex gen-server ./lexicons/generated/server/ ./lexicons/src/" 11 12 }, 12 13 "jest": { 13 14 "preset": "jest-expo" ··· 15 16 "dependencies": { 16 17 "@aquareum/atproto-oauth-client-react-native": "^0.0.1", 17 18 "@atproto/api": "^0.13.18", 19 + "@atproto/lex-cli": "^0.5.3", 18 20 "@expo/vector-icons": "^14.0.4", 19 21 "@react-native-async-storage/async-storage": "1.23.1", 20 22 "@react-navigation/native": "^7.0.9",
+72
pnpm-lock.yaml
··· 14 14 '@atproto/api': 15 15 specifier: ^0.13.18 16 16 version: 0.13.20 17 + '@atproto/lex-cli': 18 + specifier: ^0.5.3 19 + version: 0.5.3 17 20 '@expo/vector-icons': 18 21 specifier: ^14.0.4 19 22 version: 14.0.4 ··· 237 240 238 241 '@atproto/jwk@0.1.1': 239 242 resolution: {integrity: sha512-6h/bj1APUk7QcV9t/oA6+9DB5NZx9SZru9x+/pV5oHFI9Xz4ZuM5+dq1PfsJV54pZyqdnZ6W6M717cxoC7q7og==} 243 + 244 + '@atproto/lex-cli@0.5.3': 245 + resolution: {integrity: sha512-wLP8dUcW5j0+TMgEOhxC69rcZ5tEM/fED83C1FiOJMIODS3Uldez1Jpk0M7x49PeZKPTwaIefvhK3JKKXe4YiQ==} 246 + hasBin: true 240 247 241 248 '@atproto/lexicon@0.4.4': 242 249 resolution: {integrity: sha512-QFEmr3rpj/RoAmfX9ALU/asBG/rsVtQZnw+9nOB1/AuIwoxXd+ZyndR6lVUc2+DL4GEjl6W2yvBru5xbQIZWyA==} ··· 1777 1784 '@sinonjs/fake-timers@10.3.0': 1778 1785 resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} 1779 1786 1787 + '@ts-morph/common@0.17.0': 1788 + resolution: {integrity: sha512-RMSSvSfs9kb0VzkvQ2NWobwnj7TxCA9vI/IjR9bDHqgAyVbu2T0DN4wiKVqomyDWqO7dPr/tErSfq7urQ1Q37g==} 1789 + 1780 1790 '@tsconfig/node10@1.0.11': 1781 1791 resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} 1782 1792 ··· 2436 2446 resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} 2437 2447 engines: {node: '>=6'} 2438 2448 2449 + code-block-writer@11.0.3: 2450 + resolution: {integrity: sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==} 2451 + 2439 2452 color-convert@1.9.3: 2440 2453 resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 2441 2454 ··· 2477 2490 commander@7.2.0: 2478 2491 resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} 2479 2492 engines: {node: '>= 10'} 2493 + 2494 + commander@9.5.0: 2495 + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} 2496 + engines: {node: ^12.20.0 || >=14} 2480 2497 2481 2498 comment-json@4.2.5: 2482 2499 resolution: {integrity: sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==} ··· 4062 4079 minimatch@3.1.2: 4063 4080 resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 4064 4081 4082 + minimatch@5.1.6: 4083 + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} 4084 + engines: {node: '>=10'} 4085 + 4065 4086 minimatch@8.0.4: 4066 4087 resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} 4067 4088 engines: {node: '>=16 || 14 >=14.17'} ··· 4346 4367 password-prompt@1.1.3: 4347 4368 resolution: {integrity: sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw==} 4348 4369 4370 + path-browserify@1.0.1: 4371 + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} 4372 + 4349 4373 path-exists@3.0.0: 4350 4374 resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} 4351 4375 engines: {node: '>=4'} ··· 4465 4489 resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 4466 4490 engines: {node: '>= 0.8.0'} 4467 4491 4492 + prettier@3.4.2: 4493 + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} 4494 + engines: {node: '>=14'} 4495 + hasBin: true 4496 + 4468 4497 pretty-bytes@5.6.0: 4469 4498 resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} 4470 4499 engines: {node: '>=6'} ··· 5267 5296 ts-interface-checker@0.1.13: 5268 5297 resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 5269 5298 5299 + ts-morph@16.0.0: 5300 + resolution: {integrity: sha512-jGNF0GVpFj0orFw55LTsQxVYEUOCWBAbR5Ls7fTYE5pQsbW18ssTb/6UXx/GYAEjS+DQTp8VoTw0vqYMiaaQuw==} 5301 + 5270 5302 ts-node@10.9.2: 5271 5303 resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} 5272 5304 hasBin: true ··· 5655 5687 resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} 5656 5688 engines: {node: '>=12'} 5657 5689 5690 + yesno@0.4.0: 5691 + resolution: {integrity: sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA==} 5692 + 5658 5693 yn@3.1.1: 5659 5694 resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} 5660 5695 engines: {node: '>=6'} ··· 5827 5862 multiformats: 9.9.0 5828 5863 zod: 3.24.1 5829 5864 5865 + '@atproto/lex-cli@0.5.3': 5866 + dependencies: 5867 + '@atproto/lexicon': 0.4.4 5868 + '@atproto/syntax': 0.3.1 5869 + chalk: 4.1.2 5870 + commander: 9.5.0 5871 + prettier: 3.4.2 5872 + ts-morph: 16.0.0 5873 + yesno: 0.4.0 5874 + zod: 3.24.1 5875 + 5830 5876 '@atproto/lexicon@0.4.4': 5831 5877 dependencies: 5832 5878 '@atproto/common-web': 0.3.1 ··· 7906 7952 dependencies: 7907 7953 '@sinonjs/commons': 3.0.1 7908 7954 7955 + '@ts-morph/common@0.17.0': 7956 + dependencies: 7957 + fast-glob: 3.3.2 7958 + minimatch: 5.1.6 7959 + mkdirp: 1.0.4 7960 + path-browserify: 1.0.1 7961 + 7909 7962 '@tsconfig/node10@1.0.11': {} 7910 7963 7911 7964 '@tsconfig/node12@1.0.11': {} ··· 8711 8764 8712 8765 clsx@2.1.1: {} 8713 8766 8767 + code-block-writer@11.0.3: {} 8768 + 8714 8769 color-convert@1.9.3: 8715 8770 dependencies: 8716 8771 color-name: 1.1.3 ··· 8746 8801 commander@4.1.1: {} 8747 8802 8748 8803 commander@7.2.0: {} 8804 + 8805 + commander@9.5.0: {} 8749 8806 8750 8807 comment-json@4.2.5: 8751 8808 dependencies: ··· 10633 10690 dependencies: 10634 10691 brace-expansion: 1.1.11 10635 10692 10693 + minimatch@5.1.6: 10694 + dependencies: 10695 + brace-expansion: 2.0.1 10696 + 10636 10697 minimatch@8.0.4: 10637 10698 dependencies: 10638 10699 brace-expansion: 2.0.1 ··· 10904 10965 ansi-escapes: 4.3.2 10905 10966 cross-spawn: 7.0.6 10906 10967 10968 + path-browserify@1.0.1: {} 10969 + 10907 10970 path-exists@3.0.0: {} 10908 10971 10909 10972 path-exists@4.0.0: {} ··· 10992 11055 source-map-js: 1.2.1 10993 11056 10994 11057 prelude-ls@1.2.1: {} 11058 + 11059 + prettier@3.4.2: {} 10995 11060 10996 11061 pretty-bytes@5.6.0: {} 10997 11062 ··· 11936 12001 11937 12002 ts-interface-checker@0.1.13: {} 11938 12003 12004 + ts-morph@16.0.0: 12005 + dependencies: 12006 + '@ts-morph/common': 0.17.0 12007 + code-block-writer: 11.0.3 12008 + 11939 12009 ts-node@10.9.2(@types/node@22.10.2)(typescript@5.3.3): 11940 12010 dependencies: 11941 12011 '@cspotcode/source-map-support': 0.8.1 ··· 12316 12386 string-width: 4.2.3 12317 12387 y18n: 5.0.8 12318 12388 yargs-parser: 21.1.1 12389 + 12390 + yesno@0.4.0: {} 12319 12391 12320 12392 yn@3.1.1: {} 12321 12393
+47 -18
stores/authenticationSlice.tsx
··· 30 30 } 31 31 32 32 export const createAuthenticationSlice: StateCreator<AuthenticationSlice> = ( 33 - set, get 33 + set, 34 + get 34 35 ) => { 35 36 const initialAuth = createOAuthClient("http://localhost:8081"); 36 37 37 - console.log("Auth client created!") 38 + console.log("Auth client created!"); 38 39 39 40 return { 40 41 auth: initialAuth, ··· 70 71 if (!(state.has("code") && state.has("state") && state.has("iss"))) { 71 72 throw new Error("Missing params, got: " + state); 72 73 } 73 - const { auth } = get(); 74 - const { session, state: oauthState } = await auth.callback(state); 74 + // are we already logged in? 75 + if (get().status === "loggedIn") { 76 + return; 77 + } 78 + const { session, state: oauthState } = await initialAuth.callback(state); 75 79 const agent = new Agent(session); 76 80 set({ 77 81 oauthSession: session, 78 82 oauthState, 79 83 status: "loggedIn", 80 - pdsAgent: agent, 84 + pdsAgent: addDocs(agent), 81 85 isAgentReady: true, 82 86 }); 83 - } catch (error:any) { 87 + } catch (error: any) { 84 88 console.error("OAuth callback failed:", error); 85 89 set({ 86 90 status: "loggedOut", 87 91 login: { 88 92 loading: false, 89 - error: (error?.message as string) || "Unknown error during OAuth callback", 93 + error: 94 + (error?.message as string) || 95 + "Unknown error during OAuth callback", 90 96 }, 91 97 }); 92 98 } ··· 94 100 restorePdsAgent: async () => { 95 101 let did = get().oauthSession?.sub; 96 102 if (!did) { 97 - throw new Error("No session"); 103 + // 104 + // throw new Error("No session"); 105 + return; 98 106 } 99 - // restore session 100 - let sess = await initialAuth.restore(did); 107 + try { 108 + // restore session 109 + let sess = await initialAuth.restore(did); 110 + 111 + if (!sess) { 112 + throw new Error("Failed to restore session"); 113 + } 114 + 115 + const agent = new Agent(sess); 101 116 102 - if (!sess) { 103 - throw new Error("Failed to restore session"); 117 + set({ 118 + pdsAgent: addDocs(agent), 119 + isAgentReady: true, 120 + status: "loggedIn", 121 + }); 122 + console.log("Restored agent"); 123 + } catch (error) { 124 + console.error("Failed to restore agent:", error); 125 + get().logOut(); 104 126 } 105 - 106 - set({ 107 - pdsAgent: new Agent(sess), 108 - isAgentReady: true, 109 - }); 110 - console.log("Restored agent"); 111 127 }, 112 128 logOut: () => { 113 129 set({ ··· 121 137 }, 122 138 }; 123 139 }; 140 + 141 + import * as Lexicons from "../lexicons/server/lexicons"; 142 + 143 + function addDocs(agent: Agent) { 144 + Lexicons.schemas.map((schema) => { 145 + try { 146 + agent.lex.add(schema); 147 + } catch (e) { 148 + console.error("Failed to add schema:", e); 149 + } 150 + }); 151 + return agent; 152 + }