@atproto/bsky 0.0.98 → 0.0.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/api/app/bsky/unspecced/getConfig.d.ts.map +1 -1
  3. package/dist/api/app/bsky/unspecced/getConfig.js +1 -0
  4. package/dist/api/app/bsky/unspecced/getConfig.js.map +1 -1
  5. package/dist/api/app/bsky/unspecced/getTrendingTopics.d.ts +4 -0
  6. package/dist/api/app/bsky/unspecced/getTrendingTopics.d.ts.map +1 -0
  7. package/dist/api/app/bsky/unspecced/getTrendingTopics.js +52 -0
  8. package/dist/api/app/bsky/unspecced/getTrendingTopics.js.map +1 -0
  9. package/dist/api/index.d.ts.map +1 -1
  10. package/dist/api/index.js +2 -0
  11. package/dist/api/index.js.map +1 -1
  12. package/dist/config.d.ts +6 -0
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +15 -0
  15. package/dist/config.js.map +1 -1
  16. package/dist/context.d.ts +2 -0
  17. package/dist/context.d.ts.map +1 -1
  18. package/dist/context.js +3 -0
  19. package/dist/context.js.map +1 -1
  20. package/dist/hydration/actor.d.ts.map +1 -1
  21. package/dist/hydration/actor.js +6 -6
  22. package/dist/hydration/actor.js.map +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +7 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/lexicon/index.d.ts +4 -2
  27. package/dist/lexicon/index.d.ts.map +1 -1
  28. package/dist/lexicon/index.js +4 -0
  29. package/dist/lexicon/index.js.map +1 -1
  30. package/dist/lexicon/lexicons.d.ts +134 -0
  31. package/dist/lexicon/lexicons.d.ts.map +1 -1
  32. package/dist/lexicon/lexicons.js +68 -0
  33. package/dist/lexicon/lexicons.js.map +1 -1
  34. package/dist/lexicon/types/app/bsky/unspecced/defs.d.ts +9 -0
  35. package/dist/lexicon/types/app/bsky/unspecced/defs.d.ts.map +1 -1
  36. package/dist/lexicon/types/app/bsky/unspecced/defs.js +10 -0
  37. package/dist/lexicon/types/app/bsky/unspecced/defs.js.map +1 -1
  38. package/dist/lexicon/types/app/bsky/unspecced/getTrendingTopics.d.ts +39 -0
  39. package/dist/lexicon/types/app/bsky/unspecced/getTrendingTopics.d.ts.map +1 -0
  40. package/dist/lexicon/types/app/bsky/unspecced/getTrendingTopics.js +3 -0
  41. package/dist/lexicon/types/app/bsky/unspecced/getTrendingTopics.js.map +1 -0
  42. package/package.json +5 -5
  43. package/src/api/app/bsky/unspecced/getConfig.ts +1 -0
  44. package/src/api/app/bsky/unspecced/getTrendingTopics.ts +98 -0
  45. package/src/api/index.ts +2 -0
  46. package/src/config.ts +21 -0
  47. package/src/context.ts +5 -0
  48. package/src/hydration/actor.ts +9 -9
  49. package/src/index.ts +11 -0
  50. package/src/lexicon/index.ts +14 -2
  51. package/src/lexicon/lexicons.ts +69 -0
  52. package/src/lexicon/types/app/bsky/unspecced/defs.ts +20 -0
  53. package/src/lexicon/types/app/bsky/unspecced/getTrendingTopics.ts +49 -0
  54. package/tests/__snapshots__/feed-generation.test.ts.snap +16 -16
  55. package/tests/data-plane/__snapshots__/indexing.test.ts.snap +4 -4
  56. package/tests/image/sharp.test.ts +1 -1
  57. package/tests/views/__snapshots__/author-feed.test.ts.snap +40 -40
  58. package/tests/views/__snapshots__/blocks.test.ts.snap +2 -2
  59. package/tests/views/__snapshots__/list-feed.test.ts.snap +6 -6
  60. package/tests/views/__snapshots__/mute-lists.test.ts.snap +2 -2
  61. package/tests/views/__snapshots__/mutes.test.ts.snap +2 -2
  62. package/tests/views/__snapshots__/notifications.test.ts.snap +3 -3
  63. package/tests/views/__snapshots__/posts.test.ts.snap +8 -8
  64. package/tests/views/__snapshots__/quotes.test.ts.snap +2 -2
  65. package/tests/views/__snapshots__/thread.test.ts.snap +12 -12
  66. package/tests/views/__snapshots__/timeline.test.ts.snap +58 -58
  67. package/tests/views/block-lists.test.ts +1 -1
  68. package/tests/views/mute-lists.test.ts +1 -1
  69. package/tests/views/profile.test.ts +2 -2
  70. package/tsconfig.build.tsbuildinfo +1 -1
@@ -20,4 +20,13 @@ export interface SkeletonSearchStarterPack {
20
20
  }
21
21
  export declare function isSkeletonSearchStarterPack(v: unknown): v is SkeletonSearchStarterPack;
22
22
  export declare function validateSkeletonSearchStarterPack(v: unknown): ValidationResult;
23
+ export interface TrendingTopic {
24
+ topic: string;
25
+ displayName?: string;
26
+ description?: string;
27
+ link: string;
28
+ [k: string]: unknown;
29
+ }
30
+ export declare function isTrendingTopic(v: unknown): v is TrendingTopic;
31
+ export declare function validateTrendingTopic(v: unknown): ValidationResult;
23
32
  //# sourceMappingURL=defs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../../../../src/lexicon/types/app/bsky/unspecced/defs.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,gBAAgB,EAAW,MAAM,kBAAkB,CAAA;AAK5D,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACrB;AAED,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,kBAAkB,CAMxE;AAED,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAEvE;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACrB;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,mBAAmB,CAM1E;AAED,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAExE;AAED,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAA;IACX,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACrB;AAED,wBAAgB,2BAA2B,CACzC,CAAC,EAAE,OAAO,GACT,CAAC,IAAI,yBAAyB,CAMhC;AAED,wBAAgB,iCAAiC,CAC/C,CAAC,EAAE,OAAO,GACT,gBAAgB,CAKlB"}
1
+ {"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../../../../src/lexicon/types/app/bsky/unspecced/defs.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,gBAAgB,EAAW,MAAM,kBAAkB,CAAA;AAK5D,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACrB;AAED,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,kBAAkB,CAMxE;AAED,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAEvE;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACrB;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,mBAAmB,CAM1E;AAED,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAExE;AAED,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAA;IACX,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACrB;AAED,wBAAgB,2BAA2B,CACzC,CAAC,EAAE,OAAO,GACT,CAAC,IAAI,yBAAyB,CAMhC;AAED,wBAAgB,iCAAiC,CAC/C,CAAC,EAAE,OAAO,GACT,gBAAgB,CAKlB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACrB;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,aAAa,CAM9D;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAElE"}
@@ -6,6 +6,8 @@ exports.isSkeletonSearchActor = isSkeletonSearchActor;
6
6
  exports.validateSkeletonSearchActor = validateSkeletonSearchActor;
7
7
  exports.isSkeletonSearchStarterPack = isSkeletonSearchStarterPack;
8
8
  exports.validateSkeletonSearchStarterPack = validateSkeletonSearchStarterPack;
9
+ exports.isTrendingTopic = isTrendingTopic;
10
+ exports.validateTrendingTopic = validateTrendingTopic;
9
11
  const lexicons_1 = require("../../../../lexicons");
10
12
  const util_1 = require("../../../../util");
11
13
  function isSkeletonSearchPost(v) {
@@ -32,4 +34,12 @@ function isSkeletonSearchStarterPack(v) {
32
34
  function validateSkeletonSearchStarterPack(v) {
33
35
  return lexicons_1.lexicons.validate('app.bsky.unspecced.defs#skeletonSearchStarterPack', v);
34
36
  }
37
+ function isTrendingTopic(v) {
38
+ return ((0, util_1.isObj)(v) &&
39
+ (0, util_1.hasProp)(v, '$type') &&
40
+ v.$type === 'app.bsky.unspecced.defs#trendingTopic');
41
+ }
42
+ function validateTrendingTopic(v) {
43
+ return lexicons_1.lexicons.validate('app.bsky.unspecced.defs#trendingTopic', v);
44
+ }
35
45
  //# sourceMappingURL=defs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"defs.js","sourceRoot":"","sources":["../../../../../../src/lexicon/types/app/bsky/unspecced/defs.ts"],"names":[],"mappings":";;AAaA,oDAMC;AAED,gEAEC;AAOD,sDAMC;AAED,kEAEC;AAOD,kEAQC;AAED,8EAOC;AA5DD,mDAA+C;AAC/C,2CAAiD;AAQjD,SAAgB,oBAAoB,CAAC,CAAU;IAC7C,OAAO,CACL,IAAA,YAAK,EAAC,CAAC,CAAC;QACR,IAAA,cAAO,EAAC,CAAC,EAAE,OAAO,CAAC;QACnB,CAAC,CAAC,KAAK,KAAK,4CAA4C,CACzD,CAAA;AACH,CAAC;AAED,SAAgB,0BAA0B,CAAC,CAAU;IACnD,OAAO,mBAAQ,CAAC,QAAQ,CAAC,4CAA4C,EAAE,CAAC,CAAC,CAAA;AAC3E,CAAC;AAOD,SAAgB,qBAAqB,CAAC,CAAU;IAC9C,OAAO,CACL,IAAA,YAAK,EAAC,CAAC,CAAC;QACR,IAAA,cAAO,EAAC,CAAC,EAAE,OAAO,CAAC;QACnB,CAAC,CAAC,KAAK,KAAK,6CAA6C,CAC1D,CAAA;AACH,CAAC;AAED,SAAgB,2BAA2B,CAAC,CAAU;IACpD,OAAO,mBAAQ,CAAC,QAAQ,CAAC,6CAA6C,EAAE,CAAC,CAAC,CAAA;AAC5E,CAAC;AAOD,SAAgB,2BAA2B,CACzC,CAAU;IAEV,OAAO,CACL,IAAA,YAAK,EAAC,CAAC,CAAC;QACR,IAAA,cAAO,EAAC,CAAC,EAAE,OAAO,CAAC;QACnB,CAAC,CAAC,KAAK,KAAK,mDAAmD,CAChE,CAAA;AACH,CAAC;AAED,SAAgB,iCAAiC,CAC/C,CAAU;IAEV,OAAO,mBAAQ,CAAC,QAAQ,CACtB,mDAAmD,EACnD,CAAC,CACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"defs.js","sourceRoot":"","sources":["../../../../../../src/lexicon/types/app/bsky/unspecced/defs.ts"],"names":[],"mappings":";;AAaA,oDAMC;AAED,gEAEC;AAOD,sDAMC;AAED,kEAEC;AAOD,kEAQC;AAED,8EAOC;AAUD,0CAMC;AAED,sDAEC;AAhFD,mDAA+C;AAC/C,2CAAiD;AAQjD,SAAgB,oBAAoB,CAAC,CAAU;IAC7C,OAAO,CACL,IAAA,YAAK,EAAC,CAAC,CAAC;QACR,IAAA,cAAO,EAAC,CAAC,EAAE,OAAO,CAAC;QACnB,CAAC,CAAC,KAAK,KAAK,4CAA4C,CACzD,CAAA;AACH,CAAC;AAED,SAAgB,0BAA0B,CAAC,CAAU;IACnD,OAAO,mBAAQ,CAAC,QAAQ,CAAC,4CAA4C,EAAE,CAAC,CAAC,CAAA;AAC3E,CAAC;AAOD,SAAgB,qBAAqB,CAAC,CAAU;IAC9C,OAAO,CACL,IAAA,YAAK,EAAC,CAAC,CAAC;QACR,IAAA,cAAO,EAAC,CAAC,EAAE,OAAO,CAAC;QACnB,CAAC,CAAC,KAAK,KAAK,6CAA6C,CAC1D,CAAA;AACH,CAAC;AAED,SAAgB,2BAA2B,CAAC,CAAU;IACpD,OAAO,mBAAQ,CAAC,QAAQ,CAAC,6CAA6C,EAAE,CAAC,CAAC,CAAA;AAC5E,CAAC;AAOD,SAAgB,2BAA2B,CACzC,CAAU;IAEV,OAAO,CACL,IAAA,YAAK,EAAC,CAAC,CAAC;QACR,IAAA,cAAO,EAAC,CAAC,EAAE,OAAO,CAAC;QACnB,CAAC,CAAC,KAAK,KAAK,mDAAmD,CAChE,CAAA;AACH,CAAC;AAED,SAAgB,iCAAiC,CAC/C,CAAU;IAEV,OAAO,mBAAQ,CAAC,QAAQ,CACtB,mDAAmD,EACnD,CAAC,CACF,CAAA;AACH,CAAC;AAUD,SAAgB,eAAe,CAAC,CAAU;IACxC,OAAO,CACL,IAAA,YAAK,EAAC,CAAC,CAAC;QACR,IAAA,cAAO,EAAC,CAAC,EAAE,OAAO,CAAC;QACnB,CAAC,CAAC,KAAK,KAAK,uCAAuC,CACpD,CAAA;AACH,CAAC;AAED,SAAgB,qBAAqB,CAAC,CAAU;IAC9C,OAAO,mBAAQ,CAAC,QAAQ,CAAC,uCAAuC,EAAE,CAAC,CAAC,CAAA;AACtE,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express';
5
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server';
6
+ import * as AppBskyUnspeccedDefs from './defs';
7
+ export interface QueryParams {
8
+ /** DID of the account making the request (not included for public/unauthenticated queries). Used to boost followed accounts in ranking. */
9
+ viewer?: string;
10
+ limit: number;
11
+ }
12
+ export type InputSchema = undefined;
13
+ export interface OutputSchema {
14
+ topics: AppBskyUnspeccedDefs.TrendingTopic[];
15
+ suggested: AppBskyUnspeccedDefs.TrendingTopic[];
16
+ [k: string]: unknown;
17
+ }
18
+ export type HandlerInput = undefined;
19
+ export interface HandlerSuccess {
20
+ encoding: 'application/json';
21
+ body: OutputSchema;
22
+ headers?: {
23
+ [key: string]: string;
24
+ };
25
+ }
26
+ export interface HandlerError {
27
+ status: number;
28
+ message?: string;
29
+ }
30
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough;
31
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
32
+ auth: HA;
33
+ params: QueryParams;
34
+ input: HandlerInput;
35
+ req: express.Request;
36
+ res: express.Response;
37
+ };
38
+ export type Handler<HA extends HandlerAuth = never> = (ctx: HandlerReqCtx<HA>) => Promise<HandlerOutput> | HandlerOutput;
39
+ //# sourceMappingURL=getTrendingTopics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getTrendingTopics.d.ts","sourceRoot":"","sources":["../../../../../../src/lexicon/types/app/bsky/unspecced/getTrendingTopics.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,OAAO,MAAM,SAAS,CAAA;AAK7B,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACtE,OAAO,KAAK,oBAAoB,MAAM,QAAQ,CAAA;AAE9C,MAAM,WAAW,WAAW;IAC1B,2IAA2I;IAC3I,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,CAAA;AAEnC,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,oBAAoB,CAAC,aAAa,EAAE,CAAA;IAC5C,SAAS,EAAE,oBAAoB,CAAC,aAAa,EAAE,CAAA;IAC/C,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACrB;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,CAAA;AAEpC,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,IAAI,EAAE,YAAY,CAAA;IAClB,OAAO,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,kBAAkB,CAAA;AAC9E,MAAM,MAAM,aAAa,CAAC,EAAE,SAAS,WAAW,GAAG,KAAK,IAAI;IAC1D,IAAI,EAAE,EAAE,CAAA;IACR,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,YAAY,CAAA;IACnB,GAAG,EAAE,OAAO,CAAC,OAAO,CAAA;IACpB,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAA;CACtB,CAAA;AACD,MAAM,MAAM,OAAO,CAAC,EAAE,SAAS,WAAW,GAAG,KAAK,IAAI,CACpD,GAAG,EAAE,aAAa,CAAC,EAAE,CAAC,KACnB,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAAA"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=getTrendingTopics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getTrendingTopics.js","sourceRoot":"","sources":["../../../../../../src/lexicon/types/app/bsky/unspecced/getTrendingTopics.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/bsky",
3
- "version": "0.0.98",
3
+ "version": "0.0.100",
4
4
  "license": "MIT",
5
5
  "description": "Reference implementation of app.bsky App View (Bluesky API)",
6
6
  "keywords": [
@@ -42,7 +42,7 @@
42
42
  "structured-headers": "^1.0.1",
43
43
  "typed-emitter": "^2.1.0",
44
44
  "uint8arrays": "3.0.0",
45
- "@atproto/api": "^0.13.20",
45
+ "@atproto/api": "^0.13.22",
46
46
  "@atproto/common": "^0.4.5",
47
47
  "@atproto/crypto": "^0.4.2",
48
48
  "@atproto/identity": "^0.4.3",
@@ -66,9 +66,9 @@
66
66
  "jest": "^28.1.2",
67
67
  "ts-node": "^10.8.2",
68
68
  "typescript": "^5.6.3",
69
- "@atproto/api": "^0.13.20",
70
- "@atproto/lex-cli": "^0.5.3",
71
- "@atproto/pds": "^0.4.76",
69
+ "@atproto/api": "^0.13.22",
70
+ "@atproto/lex-cli": "^0.5.4",
71
+ "@atproto/pds": "^0.4.78",
72
72
  "@atproto/xrpc": "^0.6.5"
73
73
  },
74
74
  "scripts": {
@@ -9,6 +9,7 @@ export default function (server: Server, ctx: AppContext) {
9
9
  encoding: 'application/json',
10
10
  body: {
11
11
  checkEmailConfirmed: ctx.cfg.clientCheckEmailConfirmed,
12
+ topicsEnabled: ctx.cfg.topicsEnabled,
12
13
  },
13
14
  }
14
15
  },
@@ -0,0 +1,98 @@
1
+ import { noUndefinedVals } from '@atproto/common'
2
+ import AppContext from '../../../../context'
3
+ import { Server } from '../../../../lexicon'
4
+ import {
5
+ createPipeline,
6
+ HydrationFnInput,
7
+ PresentationFnInput,
8
+ RulesFnInput,
9
+ SkeletonFnInput,
10
+ } from '../../../../pipeline'
11
+ import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator'
12
+ import { Views } from '../../../../views'
13
+ import { QueryParams } from '../../../../lexicon/types/app/bsky/unspecced/getTrendingTopics'
14
+ import AtpAgent from '@atproto/api'
15
+ import { TrendingTopic } from '../../../../lexicon/types/app/bsky/unspecced/defs'
16
+ import { InternalServerError } from '@atproto/xrpc-server'
17
+
18
+ export default function (server: Server, ctx: AppContext) {
19
+ const getTrendingTopics = createPipeline(
20
+ skeleton,
21
+ hydration,
22
+ noBlocksOrMutes,
23
+ presentation,
24
+ )
25
+ server.app.bsky.unspecced.getTrendingTopics({
26
+ auth: ctx.authVerifier.standardOptional,
27
+ handler: async ({ auth, params, req }) => {
28
+ const viewer = auth.credentials.iss
29
+ const labelers = ctx.reqLabelers(req)
30
+ const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
31
+ const headers = noUndefinedVals({
32
+ 'accept-language': req.headers['accept-language'],
33
+ })
34
+ const { ...result } = await getTrendingTopics(
35
+ { ...params, hydrateCtx: hydrateCtx.copy({ viewer }), headers },
36
+ ctx,
37
+ )
38
+ return {
39
+ encoding: 'application/json',
40
+ body: result,
41
+ }
42
+ },
43
+ })
44
+ }
45
+
46
+ const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
47
+ const { params, ctx } = input
48
+ if (ctx.topicsAgent) {
49
+ const res = await ctx.topicsAgent.app.bsky.unspecced.getTrendingTopics(
50
+ {
51
+ limit: params.limit,
52
+ viewer: params.viewer,
53
+ },
54
+ {
55
+ headers: params.headers,
56
+ },
57
+ )
58
+ return res.data
59
+ } else {
60
+ throw new InternalServerError('Topics agent not available')
61
+ }
62
+ }
63
+
64
+ const hydration = async (
65
+ _: HydrationFnInput<Context, Params, SkeletonState>,
66
+ ) => {
67
+ return {}
68
+ }
69
+
70
+ const noBlocksOrMutes = (
71
+ input: RulesFnInput<Context, Params, SkeletonState>,
72
+ ) => {
73
+ const { skeleton } = input
74
+ return skeleton
75
+ }
76
+
77
+ const presentation = (
78
+ input: PresentationFnInput<Context, Params, SkeletonState>,
79
+ ) => {
80
+ const { skeleton } = input
81
+ return skeleton
82
+ }
83
+
84
+ type Context = {
85
+ hydrator: Hydrator
86
+ views: Views
87
+ topicsAgent: AtpAgent | undefined
88
+ }
89
+
90
+ type Params = QueryParams & {
91
+ hydrateCtx: HydrateCtx & { viewer: string | null }
92
+ headers: Record<string, string>
93
+ }
94
+
95
+ type SkeletonState = {
96
+ topics: TrendingTopic[]
97
+ suggested: TrendingTopic[]
98
+ }
package/src/api/index.ts CHANGED
@@ -38,6 +38,7 @@ import unmuteActorList from './app/bsky/graph/unmuteActorList'
38
38
  import muteThread from './app/bsky/graph/muteThread'
39
39
  import unmuteThread from './app/bsky/graph/unmuteThread'
40
40
  import getSuggestedFollowsByActor from './app/bsky/graph/getSuggestedFollowsByActor'
41
+ import getTrendingTopics from './app/bsky/unspecced/getTrendingTopics'
41
42
  import getLabelerServices from './app/bsky/labeler/getServices'
42
43
  import searchActors from './app/bsky/actor/searchActors'
43
44
  import searchActorsTypeahead from './app/bsky/actor/searchActorsTypeahead'
@@ -104,6 +105,7 @@ export default function (server: Server, ctx: AppContext) {
104
105
  muteThread(server, ctx)
105
106
  unmuteThread(server, ctx)
106
107
  getSuggestedFollowsByActor(server, ctx)
108
+ getTrendingTopics(server, ctx)
107
109
  getLabelerServices(server, ctx)
108
110
  searchActors(server, ctx)
109
111
  searchActorsTypeahead(server, ctx)
package/src/config.ts CHANGED
@@ -24,6 +24,8 @@ export interface ServerConfigValues {
24
24
  searchUrl?: string
25
25
  suggestionsUrl?: string
26
26
  suggestionsApiKey?: string
27
+ topicsUrl?: string
28
+ topicsApiKey?: string
27
29
  cdnUrl?: string
28
30
  videoPlaylistUrlPattern?: string
29
31
  videoThumbnailUrlPattern?: string
@@ -47,6 +49,7 @@ export interface ServerConfigValues {
47
49
  maxThreadDepth?: number
48
50
  // client config
49
51
  clientCheckEmailConfirmed?: boolean
52
+ topicsEnabled?: boolean
50
53
  }
51
54
 
52
55
  export class ServerConfig {
@@ -82,6 +85,8 @@ export class ServerConfig {
82
85
  undefined
83
86
  const suggestionsUrl = process.env.BSKY_SUGGESTIONS_URL || undefined
84
87
  const suggestionsApiKey = process.env.BSKY_SUGGESTIONS_API_KEY || undefined
88
+ const topicsUrl = process.env.BSKY_TOPICS_URL || undefined
89
+ const topicsApiKey = process.env.BSKY_TOPICS_API_KEY
85
90
  let dataplaneUrls = overrides?.dataplaneUrls
86
91
  dataplaneUrls ??= process.env.BSKY_DATAPLANE_URLS
87
92
  ? process.env.BSKY_DATAPLANE_URLS.split(',')
@@ -130,6 +135,7 @@ export class ServerConfig {
130
135
  : process.env.BSKY_STATSIG_ENV || 'development'
131
136
  const clientCheckEmailConfirmed =
132
137
  process.env.BSKY_CLIENT_CHECK_EMAIL_CONFIRMED === 'true'
138
+ const topicsEnabled = process.env.BSKY_TOPICS_ENABLED === 'true'
133
139
  const indexedAtEpoch = process.env.BSKY_INDEXED_AT_EPOCH
134
140
  ? new Date(process.env.BSKY_INDEXED_AT_EPOCH)
135
141
  : undefined
@@ -158,6 +164,8 @@ export class ServerConfig {
158
164
  searchUrl,
159
165
  suggestionsUrl,
160
166
  suggestionsApiKey,
167
+ topicsUrl,
168
+ topicsApiKey,
161
169
  didPlcUrl,
162
170
  labelsFromIssuerDids,
163
171
  handleResolveNameservers,
@@ -180,6 +188,7 @@ export class ServerConfig {
180
188
  statsigKey,
181
189
  statsigEnv,
182
190
  clientCheckEmailConfirmed,
191
+ topicsEnabled,
183
192
  indexedAtEpoch,
184
193
  bigThreadUris,
185
194
  bigThreadDepth,
@@ -285,6 +294,14 @@ export class ServerConfig {
285
294
  return this.cfg.suggestionsApiKey
286
295
  }
287
296
 
297
+ get topicsUrl() {
298
+ return this.cfg.topicsUrl
299
+ }
300
+
301
+ get topicsApiKey() {
302
+ return this.cfg.topicsApiKey
303
+ }
304
+
288
305
  get cdnUrl() {
289
306
  return this.cfg.cdnUrl
290
307
  }
@@ -341,6 +358,10 @@ export class ServerConfig {
341
358
  return this.cfg.clientCheckEmailConfirmed
342
359
  }
343
360
 
361
+ get topicsEnabled() {
362
+ return this.cfg.topicsEnabled
363
+ }
364
+
344
365
  get indexedAtEpoch() {
345
366
  return this.cfg.indexedAtEpoch
346
367
  }
package/src/context.ts CHANGED
@@ -25,6 +25,7 @@ export class AppContext {
25
25
  dataplane: DataPlaneClient
26
26
  searchAgent: AtpAgent | undefined
27
27
  suggestionsAgent: AtpAgent | undefined
28
+ topicsAgent: AtpAgent | undefined
28
29
  hydrator: Hydrator
29
30
  views: Views
30
31
  signingKey: Keypair
@@ -52,6 +53,10 @@ export class AppContext {
52
53
  return this.opts.suggestionsAgent
53
54
  }
54
55
 
56
+ get topicsAgent(): AtpAgent | undefined {
57
+ return this.opts.topicsAgent
58
+ }
59
+
55
60
  get hydrator(): Hydrator {
56
61
  return this.opts.hydrator
57
62
  }
@@ -5,7 +5,6 @@ import {
5
5
  HydrationMap,
6
6
  RecordInfo,
7
7
  parseRecord,
8
- parseRecordBytes,
9
8
  parseString,
10
9
  safeTakedownRef,
11
10
  } from './util'
@@ -116,18 +115,19 @@ export class ActorHydrator {
116
115
  ) {
117
116
  return acc.set(did, null)
118
117
  }
119
- const profile =
120
- includeTakedowns || !actor.profile?.takenDown
121
- ? actor.profile
122
- : undefined
118
+
119
+ const profile = actor.profile
120
+ ? parseRecord<ProfileRecord>(actor.profile, includeTakedowns)
121
+ : undefined
122
+
123
123
  return acc.set(did, {
124
124
  did,
125
125
  handle: parseString(actor.handle),
126
- profile: parseRecordBytes<ProfileRecord>(profile?.record),
126
+ profile: profile?.record,
127
127
  profileCid: profile?.cid,
128
- profileTakedownRef: safeTakedownRef(profile),
129
- sortedAt: profile?.sortedAt?.toDate(),
130
- indexedAt: profile?.indexedAt?.toDate(),
128
+ profileTakedownRef: profile?.takedownRef,
129
+ sortedAt: profile?.sortedAt,
130
+ indexedAt: profile?.indexedAt,
131
131
  takedownRef: safeTakedownRef(actor),
132
132
  isLabeler: actor.labeler ?? false,
133
133
  allowIncomingChatsFrom: actor.allowIncomingChatsFrom || undefined,
package/src/index.ts CHANGED
@@ -96,6 +96,16 @@ export class BskyAppView {
96
96
  )
97
97
  }
98
98
 
99
+ const topicsAgent = config.topicsUrl
100
+ ? new AtpAgent({ service: config.topicsUrl })
101
+ : undefined
102
+ if (topicsAgent && config.topicsApiKey) {
103
+ topicsAgent.api.setHeader(
104
+ 'authorization',
105
+ `Bearer ${config.topicsApiKey}`,
106
+ )
107
+ }
108
+
99
109
  const dataplane = createDataPlaneClient(config.dataplaneUrls, {
100
110
  httpVersion: config.dataplaneHttpVersion,
101
111
  rejectUnauthorized: !config.dataplaneIgnoreBadTls,
@@ -146,6 +156,7 @@ export class BskyAppView {
146
156
  dataplane,
147
157
  searchAgent,
148
158
  suggestionsAgent,
159
+ topicsAgent,
149
160
  hydrator,
150
161
  views,
151
162
  signingKey,
@@ -140,6 +140,7 @@ import * as AppBskyUnspeccedGetConfig from './types/app/bsky/unspecced/getConfig
140
140
  import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators'
141
141
  import * as AppBskyUnspeccedGetSuggestionsSkeleton from './types/app/bsky/unspecced/getSuggestionsSkeleton'
142
142
  import * as AppBskyUnspeccedGetTaggedSuggestions from './types/app/bsky/unspecced/getTaggedSuggestions'
143
+ import * as AppBskyUnspeccedGetTrendingTopics from './types/app/bsky/unspecced/getTrendingTopics'
143
144
  import * as AppBskyUnspeccedSearchActorsSkeleton from './types/app/bsky/unspecced/searchActorsSkeleton'
144
145
  import * as AppBskyUnspeccedSearchPostsSkeleton from './types/app/bsky/unspecced/searchPostsSkeleton'
145
146
  import * as AppBskyUnspeccedSearchStarterPacksSkeleton from './types/app/bsky/unspecced/searchStarterPacksSkeleton'
@@ -1853,6 +1854,17 @@ export class AppBskyUnspeccedNS {
1853
1854
  return this._server.xrpc.method(nsid, cfg)
1854
1855
  }
1855
1856
 
1857
+ getTrendingTopics<AV extends AuthVerifier>(
1858
+ cfg: ConfigOf<
1859
+ AV,
1860
+ AppBskyUnspeccedGetTrendingTopics.Handler<ExtractAuth<AV>>,
1861
+ AppBskyUnspeccedGetTrendingTopics.HandlerReqCtx<ExtractAuth<AV>>
1862
+ >,
1863
+ ) {
1864
+ const nsid = 'app.bsky.unspecced.getTrendingTopics' // @ts-ignore
1865
+ return this._server.xrpc.method(nsid, cfg)
1866
+ }
1867
+
1856
1868
  searchActorsSkeleton<AV extends AuthVerifier>(
1857
1869
  cfg: ConfigOf<
1858
1870
  AV,
@@ -2165,13 +2177,13 @@ export class ChatBskyModerationNS {
2165
2177
 
2166
2178
  type SharedRateLimitOpts<T> = {
2167
2179
  name: string
2168
- calcKey?: (ctx: T) => string
2180
+ calcKey?: (ctx: T) => string | null
2169
2181
  calcPoints?: (ctx: T) => number
2170
2182
  }
2171
2183
  type RouteRateLimitOpts<T> = {
2172
2184
  durationMs: number
2173
2185
  points: number
2174
- calcKey?: (ctx: T) => string
2186
+ calcKey?: (ctx: T) => string | null
2175
2187
  calcPoints?: (ctx: T) => number
2176
2188
  }
2177
2189
  type HandlerOpts = { blobLimit?: number }
@@ -9257,6 +9257,24 @@ export const schemaDict = {
9257
9257
  },
9258
9258
  },
9259
9259
  },
9260
+ trendingTopic: {
9261
+ type: 'object',
9262
+ required: ['topic', 'link'],
9263
+ properties: {
9264
+ topic: {
9265
+ type: 'string',
9266
+ },
9267
+ displayName: {
9268
+ type: 'string',
9269
+ },
9270
+ description: {
9271
+ type: 'string',
9272
+ },
9273
+ link: {
9274
+ type: 'string',
9275
+ },
9276
+ },
9277
+ },
9260
9278
  },
9261
9279
  },
9262
9280
  AppBskyUnspeccedGetConfig: {
@@ -9437,6 +9455,56 @@ export const schemaDict = {
9437
9455
  },
9438
9456
  },
9439
9457
  },
9458
+ AppBskyUnspeccedGetTrendingTopics: {
9459
+ lexicon: 1,
9460
+ id: 'app.bsky.unspecced.getTrendingTopics',
9461
+ defs: {
9462
+ main: {
9463
+ type: 'query',
9464
+ description: 'Get a list of trending topics',
9465
+ parameters: {
9466
+ type: 'params',
9467
+ properties: {
9468
+ viewer: {
9469
+ type: 'string',
9470
+ format: 'did',
9471
+ description:
9472
+ 'DID of the account making the request (not included for public/unauthenticated queries). Used to boost followed accounts in ranking.',
9473
+ },
9474
+ limit: {
9475
+ type: 'integer',
9476
+ minimum: 1,
9477
+ maximum: 25,
9478
+ default: 10,
9479
+ },
9480
+ },
9481
+ },
9482
+ output: {
9483
+ encoding: 'application/json',
9484
+ schema: {
9485
+ type: 'object',
9486
+ required: ['topics', 'suggested'],
9487
+ properties: {
9488
+ topics: {
9489
+ type: 'array',
9490
+ items: {
9491
+ type: 'ref',
9492
+ ref: 'lex:app.bsky.unspecced.defs#trendingTopic',
9493
+ },
9494
+ },
9495
+ suggested: {
9496
+ type: 'array',
9497
+ items: {
9498
+ type: 'ref',
9499
+ ref: 'lex:app.bsky.unspecced.defs#trendingTopic',
9500
+ },
9501
+ },
9502
+ },
9503
+ },
9504
+ },
9505
+ },
9506
+ },
9507
+ },
9440
9508
  AppBskyUnspeccedSearchActorsSkeleton: {
9441
9509
  lexicon: 1,
9442
9510
  id: 'app.bsky.unspecced.searchActorsSkeleton',
@@ -10920,6 +10988,7 @@ export const ids = {
10920
10988
  'app.bsky.unspecced.getSuggestionsSkeleton',
10921
10989
  AppBskyUnspeccedGetTaggedSuggestions:
10922
10990
  'app.bsky.unspecced.getTaggedSuggestions',
10991
+ AppBskyUnspeccedGetTrendingTopics: 'app.bsky.unspecced.getTrendingTopics',
10923
10992
  AppBskyUnspeccedSearchActorsSkeleton:
10924
10993
  'app.bsky.unspecced.searchActorsSkeleton',
10925
10994
  AppBskyUnspeccedSearchPostsSkeleton: 'app.bsky.unspecced.searchPostsSkeleton',
@@ -63,3 +63,23 @@ export function validateSkeletonSearchStarterPack(
63
63
  v,
64
64
  )
65
65
  }
66
+
67
+ export interface TrendingTopic {
68
+ topic: string
69
+ displayName?: string
70
+ description?: string
71
+ link: string
72
+ [k: string]: unknown
73
+ }
74
+
75
+ export function isTrendingTopic(v: unknown): v is TrendingTopic {
76
+ return (
77
+ isObj(v) &&
78
+ hasProp(v, '$type') &&
79
+ v.$type === 'app.bsky.unspecced.defs#trendingTopic'
80
+ )
81
+ }
82
+
83
+ export function validateTrendingTopic(v: unknown): ValidationResult {
84
+ return lexicons.validate('app.bsky.unspecced.defs#trendingTopic', v)
85
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import 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 { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+ import * as AppBskyUnspeccedDefs from './defs'
11
+
12
+ export interface QueryParams {
13
+ /** DID of the account making the request (not included for public/unauthenticated queries). Used to boost followed accounts in ranking. */
14
+ viewer?: string
15
+ limit: number
16
+ }
17
+
18
+ export type InputSchema = undefined
19
+
20
+ export interface OutputSchema {
21
+ topics: AppBskyUnspeccedDefs.TrendingTopic[]
22
+ suggested: AppBskyUnspeccedDefs.TrendingTopic[]
23
+ [k: string]: unknown
24
+ }
25
+
26
+ export type HandlerInput = undefined
27
+
28
+ export interface HandlerSuccess {
29
+ encoding: 'application/json'
30
+ body: OutputSchema
31
+ headers?: { [key: string]: string }
32
+ }
33
+
34
+ export interface HandlerError {
35
+ status: number
36
+ message?: string
37
+ }
38
+
39
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
40
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
41
+ auth: HA
42
+ params: QueryParams
43
+ input: HandlerInput
44
+ req: express.Request
45
+ res: express.Response
46
+ }
47
+ export type Handler<HA extends HandlerAuth = never> = (
48
+ ctx: HandlerReqCtx<HA>,
49
+ ) => Promise<HandlerOutput> | HandlerOutput