A decentralized music tracking and discovery platform built on AT Protocol 🎵
listenbrainz spotify atproto lastfm musicbrainz scrobbling

Add feed generators lexicon and API types

- Add feedGeneratorsView and feedGeneratorView defs (JSON and pkl)
- Add app.rocksky.feed.getFeedGenerators lexicon and generated TS types
- Export new types from lexicon index
- Swap/fix album getAlbums/getAlbumTracks handlers and update schemas
- Misc: small formatting, type-only imports, and script fixes

Changed files
+370 -82
apps
+38
apps/api/lexicons/feed/defs.json
··· 100 100 } 101 101 } 102 102 } 103 + }, 104 + "feedGeneratorsView": { 105 + "type": "object", 106 + "properties": { 107 + "feeds": { 108 + "type": "array", 109 + "items": { 110 + "type": "ref", 111 + "ref": "app.rocksky.feed.defs#feedGeneratorView" 112 + } 113 + } 114 + } 115 + }, 116 + "feedGeneratorView": { 117 + "type": "object", 118 + "properties": { 119 + "id": { 120 + "type": "string" 121 + }, 122 + "name": { 123 + "type": "string" 124 + }, 125 + "description": { 126 + "type": "string" 127 + }, 128 + "uri": { 129 + "type": "string", 130 + "format": "at-uri" 131 + }, 132 + "avatar": { 133 + "type": "string", 134 + "format": "uri" 135 + }, 136 + "creator": { 137 + "type": "ref", 138 + "ref": "app.rocksky.actor.defs#profileViewBasic" 139 + } 140 + } 103 141 } 104 142 } 105 143 }
+27
apps/api/lexicons/feed/getFeedGenerators.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.rocksky.feed.getFeedGenerators", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "Get all feed generators", 8 + "parameters": { 9 + "type": "params", 10 + "properties": { 11 + "size": { 12 + "type": "integer", 13 + "description": "The maximum number of feed generators to return.", 14 + "minimum": 1 15 + } 16 + } 17 + }, 18 + "output": { 19 + "encoding": "application/json", 20 + "schema": { 21 + "type": "ref", 22 + "ref": "app.rocksky.feed.defs#feedGeneratorsView" 23 + } 24 + } 25 + } 26 + } 27 + }
+38
apps/api/pkl/defs/feed/defs.pkl
··· 103 103 } 104 104 } 105 105 } 106 + ["feedGeneratorsView"] = new ObjectType { 107 + type = "object" 108 + properties { 109 + ["feeds"] = new Array { 110 + type = "array" 111 + items = new Ref { 112 + type = "ref" 113 + ref = "app.rocksky.feed.defs#feedGeneratorView" 114 + } 115 + } 116 + } 117 + } 118 + ["feedGeneratorView"] = new ObjectType { 119 + type = "object" 120 + properties { 121 + ["id"] = new StringType { 122 + type = "string" 123 + } 124 + ["name"] = new StringType { 125 + type = "string" 126 + } 127 + ["description"] = new StringType { 128 + type = "string" 129 + } 130 + ["uri"] = new StringType { 131 + type = "string" 132 + format = "at-uri" 133 + } 134 + ["avatar"] = new StringType { 135 + type = "string" 136 + format = "uri" 137 + } 138 + ["creator"] = new Ref { 139 + type = "ref" 140 + ref = "app.rocksky.actor.defs#profileViewBasic" 141 + } 142 + } 143 + } 106 144 }
+27
apps/api/pkl/defs/feed/getFeedGenerators.pkl
··· 1 + amends "../../schema/lexicon.pkl" 2 + 3 + lexicon = 1 4 + id = "app.rocksky.feed.getFeedGenerators" 5 + defs = new Mapping<String, Query> { 6 + ["main"] { 7 + type = "query" 8 + description = "Get all feed generators" 9 + parameters { 10 + type = "params" 11 + properties { 12 + ["size"] = new IntegerType { 13 + type = "integer" 14 + description = "The maximum number of feed generators to return." 15 + minimum = 1 16 + } 17 + } 18 + } 19 + output { 20 + encoding = "application/json" 21 + schema = new Ref { 22 + type = "ref" 23 + ref = "app.rocksky.feed.defs#feedGeneratorsView" 24 + } 25 + } 26 + } 27 + }
+30 -18
apps/api/src/lexicon/index.ts
··· 22 22 import type * as AppRockskyActorGetActorSongs from "./types/app/rocksky/actor/getActorSongs"; 23 23 import type * as AppRockskyActorGetProfile from "./types/app/rocksky/actor/getProfile"; 24 24 import type * as AppRockskyAlbumGetAlbum from "./types/app/rocksky/album/getAlbum"; 25 - import type * as AppRockskyAlbumGetAlbumTracks from "./types/app/rocksky/album/getAlbumTracks"; 26 25 import type * as AppRockskyAlbumGetAlbums from "./types/app/rocksky/album/getAlbums"; 26 + import type * as AppRockskyAlbumGetAlbumTracks from "./types/app/rocksky/album/getAlbumTracks"; 27 27 import type * as AppRockskyApikeyCreateApikey from "./types/app/rocksky/apikey/createApikey"; 28 28 import type * as AppRockskyApikeyGetApikeys from "./types/app/rocksky/apikey/getApikeys"; 29 29 import type * as AppRockskyApikeyRemoveApikey from "./types/app/rocksky/apikey/removeApikey"; ··· 31 31 import type * as AppRockskyArtistGetArtist from "./types/app/rocksky/artist/getArtist"; 32 32 import type * as AppRockskyArtistGetArtistAlbums from "./types/app/rocksky/artist/getArtistAlbums"; 33 33 import type * as AppRockskyArtistGetArtistListeners from "./types/app/rocksky/artist/getArtistListeners"; 34 + import type * as AppRockskyArtistGetArtists from "./types/app/rocksky/artist/getArtists"; 34 35 import type * as AppRockskyArtistGetArtistTracks from "./types/app/rocksky/artist/getArtistTracks"; 35 - import type * as AppRockskyArtistGetArtists from "./types/app/rocksky/artist/getArtists"; 36 36 import type * as AppRockskyChartsGetScrobblesChart from "./types/app/rocksky/charts/getScrobblesChart"; 37 37 import type * as AppRockskyDropboxDownloadFile from "./types/app/rocksky/dropbox/downloadFile"; 38 38 import type * as AppRockskyDropboxGetFiles from "./types/app/rocksky/dropbox/getFiles"; 39 39 import type * as AppRockskyDropboxGetMetadata from "./types/app/rocksky/dropbox/getMetadata"; 40 40 import type * as AppRockskyDropboxGetTemporaryLink from "./types/app/rocksky/dropbox/getTemporaryLink"; 41 + import type * as AppRockskyFeedGetFeedGenerators from "./types/app/rocksky/feed/getFeedGenerators"; 41 42 import type * as AppRockskyFeedGetNowPlayings from "./types/app/rocksky/feed/getNowPlayings"; 42 43 import type * as AppRockskyFeedSearch from "./types/app/rocksky/feed/search"; 43 44 import type * as AppRockskyGoogledriveDownloadFile from "./types/app/rocksky/googledrive/downloadFile"; ··· 365 366 return this._server.xrpc.method(nsid, cfg); 366 367 } 367 368 368 - getAlbumTracks<AV extends AuthVerifier>( 369 + getAlbums<AV extends AuthVerifier>( 369 370 cfg: ConfigOf< 370 371 AV, 371 - AppRockskyAlbumGetAlbumTracks.Handler<ExtractAuth<AV>>, 372 - AppRockskyAlbumGetAlbumTracks.HandlerReqCtx<ExtractAuth<AV>> 372 + AppRockskyAlbumGetAlbums.Handler<ExtractAuth<AV>>, 373 + AppRockskyAlbumGetAlbums.HandlerReqCtx<ExtractAuth<AV>> 373 374 >, 374 375 ) { 375 - const nsid = "app.rocksky.album.getAlbumTracks"; // @ts-ignore 376 + const nsid = "app.rocksky.album.getAlbums"; // @ts-ignore 376 377 return this._server.xrpc.method(nsid, cfg); 377 378 } 378 379 379 - getAlbums<AV extends AuthVerifier>( 380 + getAlbumTracks<AV extends AuthVerifier>( 380 381 cfg: ConfigOf< 381 382 AV, 382 - AppRockskyAlbumGetAlbums.Handler<ExtractAuth<AV>>, 383 - AppRockskyAlbumGetAlbums.HandlerReqCtx<ExtractAuth<AV>> 383 + AppRockskyAlbumGetAlbumTracks.Handler<ExtractAuth<AV>>, 384 + AppRockskyAlbumGetAlbumTracks.HandlerReqCtx<ExtractAuth<AV>> 384 385 >, 385 386 ) { 386 - const nsid = "app.rocksky.album.getAlbums"; // @ts-ignore 387 + const nsid = "app.rocksky.album.getAlbumTracks"; // @ts-ignore 387 388 return this._server.xrpc.method(nsid, cfg); 388 389 } 389 390 } ··· 480 481 return this._server.xrpc.method(nsid, cfg); 481 482 } 482 483 483 - getArtistTracks<AV extends AuthVerifier>( 484 + getArtists<AV extends AuthVerifier>( 484 485 cfg: ConfigOf< 485 486 AV, 486 - AppRockskyArtistGetArtistTracks.Handler<ExtractAuth<AV>>, 487 - AppRockskyArtistGetArtistTracks.HandlerReqCtx<ExtractAuth<AV>> 487 + AppRockskyArtistGetArtists.Handler<ExtractAuth<AV>>, 488 + AppRockskyArtistGetArtists.HandlerReqCtx<ExtractAuth<AV>> 488 489 >, 489 490 ) { 490 - const nsid = "app.rocksky.artist.getArtistTracks"; // @ts-ignore 491 + const nsid = "app.rocksky.artist.getArtists"; // @ts-ignore 491 492 return this._server.xrpc.method(nsid, cfg); 492 493 } 493 494 494 - getArtists<AV extends AuthVerifier>( 495 + getArtistTracks<AV extends AuthVerifier>( 495 496 cfg: ConfigOf< 496 497 AV, 497 - AppRockskyArtistGetArtists.Handler<ExtractAuth<AV>>, 498 - AppRockskyArtistGetArtists.HandlerReqCtx<ExtractAuth<AV>> 498 + AppRockskyArtistGetArtistTracks.Handler<ExtractAuth<AV>>, 499 + AppRockskyArtistGetArtistTracks.HandlerReqCtx<ExtractAuth<AV>> 499 500 >, 500 501 ) { 501 - const nsid = "app.rocksky.artist.getArtists"; // @ts-ignore 502 + const nsid = "app.rocksky.artist.getArtistTracks"; // @ts-ignore 502 503 return this._server.xrpc.method(nsid, cfg); 503 504 } 504 505 } ··· 579 580 580 581 constructor(server: Server) { 581 582 this._server = server; 583 + } 584 + 585 + getFeedGenerators<AV extends AuthVerifier>( 586 + cfg: ConfigOf< 587 + AV, 588 + AppRockskyFeedGetFeedGenerators.Handler<ExtractAuth<AV>>, 589 + AppRockskyFeedGetFeedGenerators.HandlerReqCtx<ExtractAuth<AV>> 590 + >, 591 + ) { 592 + const nsid = "app.rocksky.feed.getFeedGenerators"; // @ts-ignore 593 + return this._server.xrpc.method(nsid, cfg); 582 594 } 583 595 584 596 getNowPlayings<AV extends AuthVerifier>(
+113 -47
apps/api/src/lexicon/lexicons.ts
··· 1267 1267 }, 1268 1268 }, 1269 1269 }, 1270 - AppRockskyAlbumGetAlbumTracks: { 1270 + AppRockskyAlbumGetAlbums: { 1271 1271 lexicon: 1, 1272 - id: "app.rocksky.album.getAlbumTracks", 1272 + id: "app.rocksky.album.getAlbums", 1273 1273 defs: { 1274 1274 main: { 1275 1275 type: "query", 1276 - description: "Get tracks for an album", 1276 + description: "Get albums", 1277 1277 parameters: { 1278 1278 type: "params", 1279 - required: ["uri"], 1280 1279 properties: { 1281 - uri: { 1282 - type: "string", 1283 - description: "The URI of the album to retrieve tracks from", 1284 - format: "at-uri", 1280 + limit: { 1281 + type: "integer", 1282 + description: "The maximum number of albums to return", 1283 + minimum: 1, 1284 + }, 1285 + offset: { 1286 + type: "integer", 1287 + description: "The offset for pagination", 1288 + minimum: 0, 1285 1289 }, 1286 1290 }, 1287 1291 }, ··· 1290 1294 schema: { 1291 1295 type: "object", 1292 1296 properties: { 1293 - tracks: { 1297 + albums: { 1294 1298 type: "array", 1295 1299 items: { 1296 1300 type: "ref", 1297 - ref: "lex:app.rocksky.song.defs#songViewBasic", 1301 + ref: "lex:app.rocksky.album.defs#albumViewBasic", 1298 1302 }, 1299 1303 }, 1300 1304 }, ··· 1303 1307 }, 1304 1308 }, 1305 1309 }, 1306 - AppRockskyAlbumGetAlbums: { 1310 + AppRockskyAlbumGetAlbumTracks: { 1307 1311 lexicon: 1, 1308 - id: "app.rocksky.album.getAlbums", 1312 + id: "app.rocksky.album.getAlbumTracks", 1309 1313 defs: { 1310 1314 main: { 1311 1315 type: "query", 1312 - description: "Get albums", 1316 + description: "Get tracks for an album", 1313 1317 parameters: { 1314 1318 type: "params", 1319 + required: ["uri"], 1315 1320 properties: { 1316 - limit: { 1317 - type: "integer", 1318 - description: "The maximum number of albums to return", 1319 - minimum: 1, 1320 - }, 1321 - offset: { 1322 - type: "integer", 1323 - description: "The offset for pagination", 1324 - minimum: 0, 1321 + uri: { 1322 + type: "string", 1323 + description: "The URI of the album to retrieve tracks from", 1324 + format: "at-uri", 1325 1325 }, 1326 1326 }, 1327 1327 }, ··· 1330 1330 schema: { 1331 1331 type: "object", 1332 1332 properties: { 1333 - albums: { 1333 + tracks: { 1334 1334 type: "array", 1335 1335 items: { 1336 1336 type: "ref", 1337 - ref: "lex:app.rocksky.album.defs#albumViewBasic", 1337 + ref: "lex:app.rocksky.song.defs#songViewBasic", 1338 1338 }, 1339 1339 }, 1340 1340 }, ··· 1845 1845 }, 1846 1846 }, 1847 1847 }, 1848 - AppRockskyArtistGetArtistTracks: { 1848 + AppRockskyArtistGetArtists: { 1849 1849 lexicon: 1, 1850 - id: "app.rocksky.artist.getArtistTracks", 1850 + id: "app.rocksky.artist.getArtists", 1851 1851 defs: { 1852 1852 main: { 1853 1853 type: "query", 1854 - description: "Get artist's tracks", 1854 + description: "Get artists", 1855 1855 parameters: { 1856 1856 type: "params", 1857 1857 properties: { 1858 - uri: { 1859 - type: "string", 1860 - description: "The URI of the artist to retrieve albums from", 1861 - format: "at-uri", 1862 - }, 1863 1858 limit: { 1864 1859 type: "integer", 1865 - description: "The maximum number of tracks to return", 1860 + description: "The maximum number of artists to return", 1866 1861 minimum: 1, 1867 1862 }, 1868 1863 offset: { ··· 1870 1865 description: "The offset for pagination", 1871 1866 minimum: 0, 1872 1867 }, 1868 + names: { 1869 + type: "string", 1870 + description: "The names of the artists to return", 1871 + }, 1873 1872 }, 1874 1873 }, 1875 1874 output: { ··· 1877 1876 schema: { 1878 1877 type: "object", 1879 1878 properties: { 1880 - tracks: { 1879 + artists: { 1881 1880 type: "array", 1882 1881 items: { 1883 1882 type: "ref", 1884 - ref: "lex:app.rocksky.song.defs#songViewBasic", 1883 + ref: "lex:app.rocksky.artist.defs#artistViewBasic", 1885 1884 }, 1886 1885 }, 1887 1886 }, ··· 1890 1889 }, 1891 1890 }, 1892 1891 }, 1893 - AppRockskyArtistGetArtists: { 1892 + AppRockskyArtistGetArtistTracks: { 1894 1893 lexicon: 1, 1895 - id: "app.rocksky.artist.getArtists", 1894 + id: "app.rocksky.artist.getArtistTracks", 1896 1895 defs: { 1897 1896 main: { 1898 1897 type: "query", 1899 - description: "Get artists", 1898 + description: "Get artist's tracks", 1900 1899 parameters: { 1901 1900 type: "params", 1902 1901 properties: { 1902 + uri: { 1903 + type: "string", 1904 + description: "The URI of the artist to retrieve albums from", 1905 + format: "at-uri", 1906 + }, 1903 1907 limit: { 1904 1908 type: "integer", 1905 - description: "The maximum number of artists to return", 1909 + description: "The maximum number of tracks to return", 1906 1910 minimum: 1, 1907 1911 }, 1908 1912 offset: { ··· 1910 1914 description: "The offset for pagination", 1911 1915 minimum: 0, 1912 1916 }, 1913 - names: { 1914 - type: "string", 1915 - description: "The names of the artists to return", 1916 - }, 1917 1917 }, 1918 1918 }, 1919 1919 output: { ··· 1921 1921 schema: { 1922 1922 type: "object", 1923 1923 properties: { 1924 - artists: { 1924 + tracks: { 1925 1925 type: "array", 1926 1926 items: { 1927 1927 type: "ref", 1928 - ref: "lex:app.rocksky.artist.defs#artistViewBasic", 1928 + ref: "lex:app.rocksky.song.defs#songViewBasic", 1929 1929 }, 1930 1930 }, 1931 1931 }, ··· 2276 2276 }, 2277 2277 }, 2278 2278 }, 2279 + feedGeneratorsView: { 2280 + type: "object", 2281 + properties: { 2282 + feeds: { 2283 + type: "array", 2284 + items: { 2285 + type: "ref", 2286 + ref: "lex:app.rocksky.feed.defs#feedGeneratorView", 2287 + }, 2288 + }, 2289 + }, 2290 + }, 2291 + feedGeneratorView: { 2292 + type: "object", 2293 + properties: { 2294 + id: { 2295 + type: "string", 2296 + }, 2297 + name: { 2298 + type: "string", 2299 + }, 2300 + description: { 2301 + type: "string", 2302 + }, 2303 + uri: { 2304 + type: "string", 2305 + format: "at-uri", 2306 + }, 2307 + avatar: { 2308 + type: "string", 2309 + format: "uri", 2310 + }, 2311 + creator: { 2312 + type: "ref", 2313 + ref: "lex:app.rocksky.actor.defs#profileViewBasic", 2314 + }, 2315 + }, 2316 + }, 2279 2317 }, 2280 2318 }, 2281 2319 AppRockskyFeedGenerator: { ··· 2314 2352 type: "string", 2315 2353 format: "datetime", 2316 2354 }, 2355 + }, 2356 + }, 2357 + }, 2358 + }, 2359 + }, 2360 + AppRockskyFeedGetFeedGenerators: { 2361 + lexicon: 1, 2362 + id: "app.rocksky.feed.getFeedGenerators", 2363 + defs: { 2364 + main: { 2365 + type: "query", 2366 + description: "Get all feed generators", 2367 + parameters: { 2368 + type: "params", 2369 + properties: { 2370 + size: { 2371 + type: "integer", 2372 + description: "The maximum number of feed generators to return.", 2373 + minimum: 1, 2374 + }, 2375 + }, 2376 + }, 2377 + output: { 2378 + encoding: "application/json", 2379 + schema: { 2380 + type: "ref", 2381 + ref: "lex:app.rocksky.feed.defs#feedGeneratorsView", 2317 2382 }, 2318 2383 }, 2319 2384 }, ··· 5103 5168 AppRockskyAlbum: "app.rocksky.album", 5104 5169 AppRockskyAlbumDefs: "app.rocksky.album.defs", 5105 5170 AppRockskyAlbumGetAlbum: "app.rocksky.album.getAlbum", 5106 - AppRockskyAlbumGetAlbumTracks: "app.rocksky.album.getAlbumTracks", 5107 5171 AppRockskyAlbumGetAlbums: "app.rocksky.album.getAlbums", 5172 + AppRockskyAlbumGetAlbumTracks: "app.rocksky.album.getAlbumTracks", 5108 5173 AppRockskyApikeyCreateApikey: "app.rocksky.apikey.createApikey", 5109 5174 AppRockskyApikeyDefs: "app.rocksky.apikey.defs", 5110 5175 AppRockskyApikeysDefs: "app.rocksky.apikeys.defs", ··· 5116 5181 AppRockskyArtistGetArtist: "app.rocksky.artist.getArtist", 5117 5182 AppRockskyArtistGetArtistAlbums: "app.rocksky.artist.getArtistAlbums", 5118 5183 AppRockskyArtistGetArtistListeners: "app.rocksky.artist.getArtistListeners", 5184 + AppRockskyArtistGetArtists: "app.rocksky.artist.getArtists", 5119 5185 AppRockskyArtistGetArtistTracks: "app.rocksky.artist.getArtistTracks", 5120 - AppRockskyArtistGetArtists: "app.rocksky.artist.getArtists", 5121 5186 AppRockskyChartsDefs: "app.rocksky.charts.defs", 5122 5187 AppRockskyChartsGetScrobblesChart: "app.rocksky.charts.getScrobblesChart", 5123 5188 AppRockskyDropboxDefs: "app.rocksky.dropbox.defs", ··· 5127 5192 AppRockskyDropboxGetTemporaryLink: "app.rocksky.dropbox.getTemporaryLink", 5128 5193 AppRockskyFeedDefs: "app.rocksky.feed.defs", 5129 5194 AppRockskyFeedGenerator: "app.rocksky.feed.generator", 5195 + AppRockskyFeedGetFeedGenerators: "app.rocksky.feed.getFeedGenerators", 5130 5196 AppRockskyFeedGetNowPlayings: "app.rocksky.feed.getNowPlayings", 5131 5197 AppRockskyFeedSearch: "app.rocksky.feed.search", 5132 5198 AppRockskyGoogledriveDefs: "app.rocksky.googledrive.defs",
+39
apps/api/src/lexicon/types/app/rocksky/feed/defs.ts
··· 86 86 export function validateNowPlayingsView(v: unknown): ValidationResult { 87 87 return lexicons.validate("app.rocksky.feed.defs#nowPlayingsView", v); 88 88 } 89 + 90 + export interface FeedGeneratorsView { 91 + feeds?: FeedGeneratorView[]; 92 + [k: string]: unknown; 93 + } 94 + 95 + export function isFeedGeneratorsView(v: unknown): v is FeedGeneratorsView { 96 + return ( 97 + isObj(v) && 98 + hasProp(v, "$type") && 99 + v.$type === "app.rocksky.feed.defs#feedGeneratorsView" 100 + ); 101 + } 102 + 103 + export function validateFeedGeneratorsView(v: unknown): ValidationResult { 104 + return lexicons.validate("app.rocksky.feed.defs#feedGeneratorsView", v); 105 + } 106 + 107 + export interface FeedGeneratorView { 108 + id?: string; 109 + name?: string; 110 + description?: string; 111 + uri?: string; 112 + avatar?: string; 113 + creator?: AppRockskyActorDefs.ProfileViewBasic; 114 + [k: string]: unknown; 115 + } 116 + 117 + export function isFeedGeneratorView(v: unknown): v is FeedGeneratorView { 118 + return ( 119 + isObj(v) && 120 + hasProp(v, "$type") && 121 + v.$type === "app.rocksky.feed.defs#feedGeneratorView" 122 + ); 123 + } 124 + 125 + export function validateFeedGeneratorView(v: unknown): ValidationResult { 126 + return lexicons.validate("app.rocksky.feed.defs#feedGeneratorView", v); 127 + }
+43
apps/api/src/lexicon/types/app/rocksky/feed/getFeedGenerators.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import type express from "express"; 5 + import { ValidationResult, BlobRef } from "@atproto/lexicon"; 6 + import { lexicons } from "../../../../lexicons"; 7 + import { isObj, hasProp } from "../../../../util"; 8 + import { CID } from "multiformats/cid"; 9 + import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 + import type * as AppRockskyFeedDefs from "./defs"; 11 + 12 + export interface QueryParams { 13 + /** The maximum number of feed generators to return. */ 14 + size?: number; 15 + } 16 + 17 + export type InputSchema = undefined; 18 + export type OutputSchema = AppRockskyFeedDefs.FeedGeneratorsView; 19 + export type HandlerInput = undefined; 20 + 21 + export interface HandlerSuccess { 22 + encoding: "application/json"; 23 + body: OutputSchema; 24 + headers?: { [key: string]: string }; 25 + } 26 + 27 + export interface HandlerError { 28 + status: number; 29 + message?: string; 30 + } 31 + 32 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough; 33 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 34 + auth: HA; 35 + params: QueryParams; 36 + input: HandlerInput; 37 + req: express.Request; 38 + res: express.Response; 39 + resetRouteRateLimits: () => Promise<void>; 40 + }; 41 + export type Handler<HA extends HandlerAuth = never> = ( 42 + ctx: HandlerReqCtx<HA>, 43 + ) => Promise<HandlerOutput> | HandlerOutput;
+1 -3
apps/api/src/schema/feeds.ts
··· 4 4 import users from "./users"; 5 5 6 6 const feeds = pgTable("feeds", { 7 - id: text("xata_id") 8 - .primaryKey() 9 - .default(sql`xata_id()`), 7 + id: text("xata_id").primaryKey().default(sql`xata_id()`), 10 8 displayName: text("display_name").notNull(), 11 9 description: text("description"), 12 10 did: text("did").notNull(),
+4 -4
apps/api/src/scripts/avatar.ts
··· 10 10 async function processUser(user: SelectUser) { 11 11 if (!process.env.SKIP_AVATAR_UPDATE) { 12 12 const plc = await fetch(`https://plc.directory/${user.did}`).then((res) => 13 - res.json() 13 + res.json(), 14 14 ); 15 15 16 16 const serviceEndpoint = _.get(plc, "service.0.serviceEndpoint"); ··· 20 20 } 21 21 22 22 const profile = await fetch( 23 - `${serviceEndpoint}/xrpc/com.atproto.repo.getRecord?repo=${user.did}&collection=app.bsky.actor.profile&rkey=self` 23 + `${serviceEndpoint}/xrpc/com.atproto.repo.getRecord?repo=${user.did}&collection=app.bsky.actor.profile&rkey=self`, 24 24 ).then((res) => res.json()); 25 25 const ref = _.get(profile, "value.avatar.ref.$link"); 26 26 const type = _.get(profile, "value.avatar.mimeType", "").split("/")[1]; ··· 57 57 console.log(userPayload); 58 58 await ctx.nc.publish( 59 59 "rocksky.user", 60 - Buffer.from(JSON.stringify(userPayload)) 60 + Buffer.from(JSON.stringify(userPayload)), 61 61 ); 62 62 } 63 63 ··· 95 95 } 96 96 97 97 console.log( 98 - `Processing batch ${Math.floor(offset / BATCH_SIZE) + 1}, users ${offset + 1}-${offset + batch.length}` 98 + `Processing batch ${Math.floor(offset / BATCH_SIZE) + 1}, users ${offset + 1}-${offset + batch.length}`, 99 99 ); 100 100 101 101 for (const user of batch) {
+5 -5
apps/api/src/scripts/feed.ts
··· 1 1 import chalk from "chalk"; 2 2 import { ctx } from "context"; 3 - import * as FeedGenerator from "lexicon/types/app/rocksky/feed/generator"; 3 + import type * as FeedGenerator from "lexicon/types/app/rocksky/feed/generator"; 4 4 import { createAgent } from "lib/agent"; 5 5 import prompts from "prompts"; 6 6 ··· 42 42 43 43 if (!/^did:web:[a-zA-Z0-9_.-]{3,30}$/.test(did.value)) { 44 44 console.error( 45 - "Invalid DID format. It should start with 'did:web:' followed by 3 to 30 alphanumeric characters, underscores, hyphens, or periods." 45 + "Invalid DID format. It should start with 'did:web:' followed by 3 to 30 alphanumeric characters, underscores, hyphens, or periods.", 46 46 ); 47 47 process.exit(1); 48 48 } ··· 55 55 56 56 if (!/^[a-zA-Z0-9_-]{3,30}$/.test(rkey.value)) { 57 57 console.error( 58 - "Invalid record key. Only alphanumeric characters, underscores, and hyphens are allowed. Length must be between 3 and 30 characters." 58 + "Invalid record key. Only alphanumeric characters, underscores, and hyphens are allowed. Length must be between 3 and 30 characters.", 59 59 ); 60 60 process.exit(1); 61 61 } ··· 67 67 console.log("DID:", did.value); 68 68 console.log("Record key (rkey):", rkey.value); 69 69 70 - let confirm = await prompts({ 70 + const confirm = await prompts({ 71 71 type: "confirm", 72 72 name: "value", 73 73 message: "Do you want to proceed?", ··· 88 88 const agent = await createAgent(ctx.oauthClient, userDid); 89 89 90 90 console.log( 91 - `Writing ${chalk.greenBright("app.rocksky.feed.generator")} record...` 91 + `Writing ${chalk.greenBright("app.rocksky.feed.generator")} record...`, 92 92 ); 93 93 94 94 const record: FeedGenerator.Record = {
+1 -1
apps/api/src/scripts/seed-feed.ts
··· 4 4 import { createAgent } from "lib/agent"; 5 5 import * as FeedGenerator from "lexicon/types/app/rocksky/feed/generator"; 6 6 import tables from "schema"; 7 - import { InsertFeed } from "schema/feeds"; 7 + import type { InsertFeed } from "schema/feeds"; 8 8 import { eq } from "drizzle-orm"; 9 9 10 10 const args = process.argv.slice(2);
+4 -4
apps/api/src/sqliteKv.ts
··· 100 100 oc.column("id").doUpdateSet({ 101 101 value, 102 102 updated_at: now, 103 - }) 103 + }), 104 104 ) 105 105 .execute(); 106 106 }, ··· 125 125 oc.column("id").doUpdateSet({ 126 126 value, 127 127 updated_at: now, 128 - }) 128 + }), 129 129 ) 130 130 .execute(); 131 - }) 131 + }), 132 132 ); 133 133 }); 134 134 }, ··· 169 169 await getDb().destroy(); 170 170 }, 171 171 }; 172 - } 172 + }, 173 173 );