@atproto/bsky 0.0.184 → 0.0.186

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 (51) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.d.ts +4 -0
  3. package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.d.ts.map +1 -0
  4. package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.js +101 -0
  5. package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.js.map +1 -0
  6. package/dist/api/index.d.ts.map +1 -1
  7. package/dist/api/index.js +2 -0
  8. package/dist/api/index.js.map +1 -1
  9. package/dist/lexicon/index.d.ts +4 -0
  10. package/dist/lexicon/index.d.ts.map +1 -1
  11. package/dist/lexicon/index.js +8 -0
  12. package/dist/lexicon/index.js.map +1 -1
  13. package/dist/lexicon/lexicons.d.ts +192 -0
  14. package/dist/lexicon/lexicons.d.ts.map +1 -1
  15. package/dist/lexicon/lexicons.js +98 -0
  16. package/dist/lexicon/lexicons.js.map +1 -1
  17. package/dist/lexicon/types/app/bsky/actor/defs.d.ts +2 -0
  18. package/dist/lexicon/types/app/bsky/actor/defs.d.ts.map +1 -1
  19. package/dist/lexicon/types/app/bsky/actor/defs.js.map +1 -1
  20. package/dist/lexicon/types/app/bsky/actor/profile.d.ts +3 -0
  21. package/dist/lexicon/types/app/bsky/actor/profile.d.ts.map +1 -1
  22. package/dist/lexicon/types/app/bsky/actor/profile.js.map +1 -1
  23. package/dist/lexicon/types/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.d.ts +22 -0
  24. package/dist/lexicon/types/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.d.ts.map +1 -0
  25. package/dist/lexicon/types/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.js +7 -0
  26. package/dist/lexicon/types/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.js.map +1 -0
  27. package/dist/lexicon/types/app/bsky/unspecced/getOnboardingSuggestedStarterPacksSkeleton.d.ts +23 -0
  28. package/dist/lexicon/types/app/bsky/unspecced/getOnboardingSuggestedStarterPacksSkeleton.d.ts.map +1 -0
  29. package/dist/lexicon/types/app/bsky/unspecced/getOnboardingSuggestedStarterPacksSkeleton.js +7 -0
  30. package/dist/lexicon/types/app/bsky/unspecced/getOnboardingSuggestedStarterPacksSkeleton.js.map +1 -0
  31. package/dist/util.d.ts.map +1 -1
  32. package/dist/util.js +3 -1
  33. package/dist/util.js.map +1 -1
  34. package/dist/views/index.d.ts +1 -0
  35. package/dist/views/index.d.ts.map +1 -1
  36. package/dist/views/index.js +11 -0
  37. package/dist/views/index.js.map +1 -1
  38. package/package.json +6 -6
  39. package/src/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts +152 -0
  40. package/src/api/index.ts +2 -0
  41. package/src/lexicon/index.ts +26 -0
  42. package/src/lexicon/lexicons.ts +102 -0
  43. package/src/lexicon/types/app/bsky/actor/defs.ts +2 -0
  44. package/src/lexicon/types/app/bsky/actor/profile.ts +3 -0
  45. package/src/lexicon/types/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts +40 -0
  46. package/src/lexicon/types/app/bsky/unspecced/getOnboardingSuggestedStarterPacksSkeleton.ts +41 -0
  47. package/src/util.ts +3 -1
  48. package/src/views/index.ts +12 -0
  49. package/tests/views/__snapshots__/profile.test.ts.snap +54 -0
  50. package/tests/views/profile.test.ts +43 -0
  51. package/tsconfig.build.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/bsky",
3
- "version": "0.0.184",
3
+ "version": "0.0.186",
4
4
  "license": "MIT",
5
5
  "description": "Reference implementation of app.bsky App View (Bluesky API)",
6
6
  "keywords": [
@@ -53,14 +53,14 @@
53
53
  "zod": "3.23.8",
54
54
  "@atproto-labs/fetch-node": "0.1.10",
55
55
  "@atproto-labs/xrpc-utils": "0.0.22",
56
- "@atproto/api": "^0.16.9",
56
+ "@atproto/api": "^0.16.11",
57
57
  "@atproto/common": "^0.4.12",
58
58
  "@atproto/crypto": "^0.4.4",
59
59
  "@atproto/did": "^0.2.0",
60
60
  "@atproto/identity": "^0.4.9",
61
61
  "@atproto/lexicon": "^0.5.1",
62
- "@atproto/repo": "^0.8.9",
63
- "@atproto/sync": "^0.1.34",
62
+ "@atproto/repo": "^0.8.10",
63
+ "@atproto/sync": "^0.1.35",
64
64
  "@atproto/syntax": "^0.4.1",
65
65
  "@atproto/xrpc-server": "^0.9.5"
66
66
  },
@@ -77,9 +77,9 @@
77
77
  "jest": "^28.1.2",
78
78
  "ts-node": "^10.8.2",
79
79
  "typescript": "^5.6.3",
80
- "@atproto/api": "^0.16.9",
80
+ "@atproto/api": "^0.16.11",
81
81
  "@atproto/lex-cli": "^0.9.5",
82
- "@atproto/pds": "^0.4.178",
82
+ "@atproto/pds": "^0.4.182",
83
83
  "@atproto/xrpc": "^0.7.5"
84
84
  },
85
85
  "scripts": {
@@ -0,0 +1,152 @@
1
+ import AtpAgent, { AtUri } from '@atproto/api'
2
+ import { dedupeStrs, mapDefined, noUndefinedVals } from '@atproto/common'
3
+ import { InternalServerError } from '@atproto/xrpc-server'
4
+ import { AppContext } from '../../../../context'
5
+ import {
6
+ HydrateCtx,
7
+ Hydrator,
8
+ mergeManyStates,
9
+ } from '../../../../hydration/hydrator'
10
+ import { Server } from '../../../../lexicon'
11
+ import { QueryParams } from '../../../../lexicon/types/app/bsky/unspecced/getTrendingTopics'
12
+ import {
13
+ HydrationFnInput,
14
+ PresentationFnInput,
15
+ RulesFnInput,
16
+ SkeletonFnInput,
17
+ createPipeline,
18
+ } from '../../../../pipeline'
19
+ import { Views } from '../../../../views'
20
+
21
+ export default function (server: Server, ctx: AppContext) {
22
+ const getOnboardingSuggestedStarterPacks = createPipeline(
23
+ skeleton,
24
+ hydration,
25
+ noBlocks,
26
+ presentation,
27
+ )
28
+ server.app.bsky.unspecced.getOnboardingSuggestedStarterPacks({
29
+ auth: ctx.authVerifier.standardOptional,
30
+ handler: async ({ auth, params, req }) => {
31
+ const viewer = auth.credentials.iss
32
+ const labelers = ctx.reqLabelers(req)
33
+ const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
34
+ const headers = noUndefinedVals({
35
+ 'accept-language': req.headers['accept-language'],
36
+ 'x-bsky-topics': Array.isArray(req.headers['x-bsky-topics'])
37
+ ? req.headers['x-bsky-topics'].join(',')
38
+ : req.headers['x-bsky-topics'],
39
+ })
40
+ const { ...result } = await getOnboardingSuggestedStarterPacks(
41
+ {
42
+ ...params,
43
+ viewer: viewer ?? undefined,
44
+ hydrateCtx: hydrateCtx.copy({ viewer }),
45
+ headers,
46
+ },
47
+ ctx,
48
+ )
49
+ return {
50
+ encoding: 'application/json',
51
+ body: result,
52
+ }
53
+ },
54
+ })
55
+ }
56
+
57
+ const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
58
+ const { params, ctx } = input
59
+ if (ctx.topicsAgent) {
60
+ const res =
61
+ await ctx.topicsAgent.app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton(
62
+ {
63
+ limit: params.limit,
64
+ viewer: params.viewer,
65
+ },
66
+ {
67
+ headers: params.headers,
68
+ },
69
+ )
70
+
71
+ return res.data
72
+ } else {
73
+ throw new InternalServerError('Topics agent not available')
74
+ }
75
+ }
76
+
77
+ const hydration = async (
78
+ input: HydrationFnInput<Context, Params, SkeletonState>,
79
+ ) => {
80
+ const { ctx, params, skeleton } = input
81
+ let dids: string[] = []
82
+ for (const uri of skeleton.starterPacks) {
83
+ let aturi: AtUri | undefined
84
+ try {
85
+ aturi = new AtUri(uri)
86
+ } catch {
87
+ continue
88
+ }
89
+ dids.push(aturi.hostname)
90
+ }
91
+ dids = dedupeStrs(dids)
92
+ const pairs: Map<string, string[]> = new Map()
93
+ if (params.viewer) {
94
+ pairs.set(params.viewer, dids)
95
+ }
96
+ const [starterPacksState, bidirectionalBlocks] = await Promise.all([
97
+ ctx.hydrator.hydrateStarterPacks(skeleton.starterPacks, params.hydrateCtx),
98
+ ctx.hydrator.hydrateBidirectionalBlocks(pairs, params.hydrateCtx),
99
+ ])
100
+
101
+ return mergeManyStates(starterPacksState, { bidirectionalBlocks })
102
+ }
103
+
104
+ const noBlocks = (input: RulesFnInput<Context, Params, SkeletonState>) => {
105
+ const { skeleton, params, hydration } = input
106
+
107
+ if (!params.viewer) {
108
+ return skeleton
109
+ }
110
+
111
+ const blocks = hydration.bidirectionalBlocks?.get(params.viewer)
112
+ const filteredSkeleton: SkeletonState = {
113
+ starterPacks: skeleton.starterPacks.filter((uri) => {
114
+ let aturi: AtUri | undefined
115
+ try {
116
+ aturi = new AtUri(uri)
117
+ } catch {
118
+ return false
119
+ }
120
+ return !blocks?.get(aturi.hostname)
121
+ }),
122
+ }
123
+
124
+ return filteredSkeleton
125
+ }
126
+
127
+ const presentation = (
128
+ input: PresentationFnInput<Context, Params, SkeletonState>,
129
+ ) => {
130
+ const { ctx, skeleton, hydration } = input
131
+
132
+ return {
133
+ starterPacks: mapDefined(skeleton.starterPacks, (uri) =>
134
+ ctx.views.starterPack(uri, hydration),
135
+ ),
136
+ }
137
+ }
138
+
139
+ type Context = {
140
+ hydrator: Hydrator
141
+ views: Views
142
+ topicsAgent: AtpAgent | undefined
143
+ }
144
+
145
+ type Params = QueryParams & {
146
+ hydrateCtx: HydrateCtx & { viewer: string | null }
147
+ headers: Record<string, string>
148
+ }
149
+
150
+ type SkeletonState = {
151
+ starterPacks: string[]
152
+ }
package/src/api/index.ts CHANGED
@@ -58,6 +58,7 @@ import registerPush from './app/bsky/notification/registerPush'
58
58
  import updateSeen from './app/bsky/notification/updateSeen'
59
59
  import getAgeAssuranceState from './app/bsky/unspecced/getAgeAssuranceState'
60
60
  import getConfig from './app/bsky/unspecced/getConfig'
61
+ import getOnboardingSuggestedStarterPacks from './app/bsky/unspecced/getOnboardingSuggestedStarterPacks'
61
62
  import getPopularFeedGenerators from './app/bsky/unspecced/getPopularFeedGenerators'
62
63
  import getPostThreadOtherV2 from './app/bsky/unspecced/getPostThreadOtherV2'
63
64
  import getPostThreadV2 from './app/bsky/unspecced/getPostThreadV2'
@@ -133,6 +134,7 @@ export default function (server: Server, ctx: AppContext) {
133
134
  getSuggestedFollowsByActor(server, ctx)
134
135
  getTrendingTopics(server, ctx)
135
136
  getTrends(server, ctx)
137
+ getOnboardingSuggestedStarterPacks(server, ctx)
136
138
  getSuggestedStarterPacks(server, ctx)
137
139
  getSuggestedUsers(server, ctx)
138
140
  getUnspeccedSuggestedFeeds(server, ctx)
@@ -74,6 +74,8 @@ import * as AppBskyNotificationUnregisterPush from './types/app/bsky/notificatio
74
74
  import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen.js'
75
75
  import * as AppBskyUnspeccedGetAgeAssuranceState from './types/app/bsky/unspecced/getAgeAssuranceState.js'
76
76
  import * as AppBskyUnspeccedGetConfig from './types/app/bsky/unspecced/getConfig.js'
77
+ import * as AppBskyUnspeccedGetOnboardingSuggestedStarterPacks from './types/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.js'
78
+ import * as AppBskyUnspeccedGetOnboardingSuggestedStarterPacksSkeleton from './types/app/bsky/unspecced/getOnboardingSuggestedStarterPacksSkeleton.js'
77
79
  import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators.js'
78
80
  import * as AppBskyUnspeccedGetPostThreadOtherV2 from './types/app/bsky/unspecced/getPostThreadOtherV2.js'
79
81
  import * as AppBskyUnspeccedGetPostThreadV2 from './types/app/bsky/unspecced/getPostThreadV2.js'
@@ -1132,6 +1134,30 @@ export class AppBskyUnspeccedNS {
1132
1134
  return this._server.xrpc.method(nsid, cfg)
1133
1135
  }
1134
1136
 
1137
+ getOnboardingSuggestedStarterPacks<A extends Auth = void>(
1138
+ cfg: MethodConfigOrHandler<
1139
+ A,
1140
+ AppBskyUnspeccedGetOnboardingSuggestedStarterPacks.QueryParams,
1141
+ AppBskyUnspeccedGetOnboardingSuggestedStarterPacks.HandlerInput,
1142
+ AppBskyUnspeccedGetOnboardingSuggestedStarterPacks.HandlerOutput
1143
+ >,
1144
+ ) {
1145
+ const nsid = 'app.bsky.unspecced.getOnboardingSuggestedStarterPacks' // @ts-ignore
1146
+ return this._server.xrpc.method(nsid, cfg)
1147
+ }
1148
+
1149
+ getOnboardingSuggestedStarterPacksSkeleton<A extends Auth = void>(
1150
+ cfg: MethodConfigOrHandler<
1151
+ A,
1152
+ AppBskyUnspeccedGetOnboardingSuggestedStarterPacksSkeleton.QueryParams,
1153
+ AppBskyUnspeccedGetOnboardingSuggestedStarterPacksSkeleton.HandlerInput,
1154
+ AppBskyUnspeccedGetOnboardingSuggestedStarterPacksSkeleton.HandlerOutput
1155
+ >,
1156
+ ) {
1157
+ const nsid = 'app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton' // @ts-ignore
1158
+ return this._server.xrpc.method(nsid, cfg)
1159
+ }
1160
+
1135
1161
  getPopularFeedGenerators<A extends Auth = void>(
1136
1162
  cfg: MethodConfigOrHandler<
1137
1163
  A,
@@ -145,6 +145,13 @@ export const schemaDict = {
145
145
  maxGraphemes: 256,
146
146
  maxLength: 2560,
147
147
  },
148
+ pronouns: {
149
+ type: 'string',
150
+ },
151
+ website: {
152
+ type: 'string',
153
+ format: 'uri',
154
+ },
148
155
  avatar: {
149
156
  type: 'string',
150
157
  format: 'uri',
@@ -964,6 +971,16 @@ export const schemaDict = {
964
971
  maxGraphemes: 256,
965
972
  maxLength: 2560,
966
973
  },
974
+ pronouns: {
975
+ type: 'string',
976
+ description: 'Free-form pronouns text.',
977
+ maxGraphemes: 20,
978
+ maxLength: 200,
979
+ },
980
+ website: {
981
+ type: 'string',
982
+ format: 'uri',
983
+ },
967
984
  avatar: {
968
985
  type: 'blob',
969
986
  description:
@@ -6611,6 +6628,87 @@ export const schemaDict = {
6611
6628
  },
6612
6629
  },
6613
6630
  },
6631
+ AppBskyUnspeccedGetOnboardingSuggestedStarterPacks: {
6632
+ lexicon: 1,
6633
+ id: 'app.bsky.unspecced.getOnboardingSuggestedStarterPacks',
6634
+ defs: {
6635
+ main: {
6636
+ type: 'query',
6637
+ description: 'Get a list of suggested starterpacks for onboarding',
6638
+ parameters: {
6639
+ type: 'params',
6640
+ properties: {
6641
+ limit: {
6642
+ type: 'integer',
6643
+ minimum: 1,
6644
+ maximum: 25,
6645
+ default: 10,
6646
+ },
6647
+ },
6648
+ },
6649
+ output: {
6650
+ encoding: 'application/json',
6651
+ schema: {
6652
+ type: 'object',
6653
+ required: ['starterPacks'],
6654
+ properties: {
6655
+ starterPacks: {
6656
+ type: 'array',
6657
+ items: {
6658
+ type: 'ref',
6659
+ ref: 'lex:app.bsky.graph.defs#starterPackView',
6660
+ },
6661
+ },
6662
+ },
6663
+ },
6664
+ },
6665
+ },
6666
+ },
6667
+ },
6668
+ AppBskyUnspeccedGetOnboardingSuggestedStarterPacksSkeleton: {
6669
+ lexicon: 1,
6670
+ id: 'app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton',
6671
+ defs: {
6672
+ main: {
6673
+ type: 'query',
6674
+ description:
6675
+ 'Get a skeleton of suggested starterpacks for onboarding. Intended to be called and hydrated by app.bsky.unspecced.getOnboardingSuggestedStarterPacks',
6676
+ parameters: {
6677
+ type: 'params',
6678
+ properties: {
6679
+ viewer: {
6680
+ type: 'string',
6681
+ format: 'did',
6682
+ description:
6683
+ 'DID of the account making the request (not included for public/unauthenticated queries).',
6684
+ },
6685
+ limit: {
6686
+ type: 'integer',
6687
+ minimum: 1,
6688
+ maximum: 25,
6689
+ default: 10,
6690
+ },
6691
+ },
6692
+ },
6693
+ output: {
6694
+ encoding: 'application/json',
6695
+ schema: {
6696
+ type: 'object',
6697
+ required: ['starterPacks'],
6698
+ properties: {
6699
+ starterPacks: {
6700
+ type: 'array',
6701
+ items: {
6702
+ type: 'string',
6703
+ format: 'at-uri',
6704
+ },
6705
+ },
6706
+ },
6707
+ },
6708
+ },
6709
+ },
6710
+ },
6711
+ },
6614
6712
  AppBskyUnspeccedGetPopularFeedGenerators: {
6615
6713
  lexicon: 1,
6616
6714
  id: 'app.bsky.unspecced.getPopularFeedGenerators',
@@ -13962,6 +14060,10 @@ export const ids = {
13962
14060
  AppBskyUnspeccedGetAgeAssuranceState:
13963
14061
  'app.bsky.unspecced.getAgeAssuranceState',
13964
14062
  AppBskyUnspeccedGetConfig: 'app.bsky.unspecced.getConfig',
14063
+ AppBskyUnspeccedGetOnboardingSuggestedStarterPacks:
14064
+ 'app.bsky.unspecced.getOnboardingSuggestedStarterPacks',
14065
+ AppBskyUnspeccedGetOnboardingSuggestedStarterPacksSkeleton:
14066
+ 'app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton',
13965
14067
  AppBskyUnspeccedGetPopularFeedGenerators:
13966
14068
  'app.bsky.unspecced.getPopularFeedGenerators',
13967
14069
  AppBskyUnspeccedGetPostThreadOtherV2:
@@ -77,6 +77,8 @@ export interface ProfileViewDetailed {
77
77
  handle: string
78
78
  displayName?: string
79
79
  description?: string
80
+ pronouns?: string
81
+ website?: string
80
82
  avatar?: string
81
83
  banner?: string
82
84
  followersCount?: number
@@ -21,6 +21,9 @@ export interface Record {
21
21
  displayName?: string
22
22
  /** Free-form profile description text. */
23
23
  description?: string
24
+ /** Free-form pronouns text. */
25
+ pronouns?: string
26
+ website?: string
24
27
  /** Small image to be displayed next to posts from account. AKA, 'profile picture' */
25
28
  avatar?: BlobRef
26
29
  /** Larger horizontal image to display behind profile view. */
@@ -0,0 +1,40 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import { type ValidationResult, BlobRef } from '@atproto/lexicon'
5
+ import { CID } from 'multiformats/cid'
6
+ import { validate as _validate } from '../../../../lexicons'
7
+ import {
8
+ type $Typed,
9
+ is$typed as _is$typed,
10
+ type OmitKey,
11
+ } from '../../../../util'
12
+ import type * as AppBskyGraphDefs from '../graph/defs.js'
13
+
14
+ const is$typed = _is$typed,
15
+ validate = _validate
16
+ const id = 'app.bsky.unspecced.getOnboardingSuggestedStarterPacks'
17
+
18
+ export type QueryParams = {
19
+ limit: number
20
+ }
21
+ export type InputSchema = undefined
22
+
23
+ export interface OutputSchema {
24
+ starterPacks: AppBskyGraphDefs.StarterPackView[]
25
+ }
26
+
27
+ export type HandlerInput = void
28
+
29
+ export interface HandlerSuccess {
30
+ encoding: 'application/json'
31
+ body: OutputSchema
32
+ headers?: { [key: string]: string }
33
+ }
34
+
35
+ export interface HandlerError {
36
+ status: number
37
+ message?: string
38
+ }
39
+
40
+ export type HandlerOutput = HandlerError | HandlerSuccess
@@ -0,0 +1,41 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import { type ValidationResult, BlobRef } from '@atproto/lexicon'
5
+ import { CID } from 'multiformats/cid'
6
+ import { validate as _validate } from '../../../../lexicons'
7
+ import {
8
+ type $Typed,
9
+ is$typed as _is$typed,
10
+ type OmitKey,
11
+ } from '../../../../util'
12
+
13
+ const is$typed = _is$typed,
14
+ validate = _validate
15
+ const id = 'app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton'
16
+
17
+ export type QueryParams = {
18
+ /** DID of the account making the request (not included for public/unauthenticated queries). */
19
+ viewer?: string
20
+ limit: number
21
+ }
22
+ export type InputSchema = undefined
23
+
24
+ export interface OutputSchema {
25
+ starterPacks: string[]
26
+ }
27
+
28
+ export type HandlerInput = void
29
+
30
+ export interface HandlerSuccess {
31
+ encoding: 'application/json'
32
+ body: OutputSchema
33
+ headers?: { [key: string]: string }
34
+ }
35
+
36
+ export interface HandlerError {
37
+ status: number
38
+ message?: string
39
+ }
40
+
41
+ export type HandlerOutput = HandlerError | HandlerSuccess
package/src/util.ts CHANGED
@@ -8,7 +8,9 @@ export type ParsedLabelers = {
8
8
  export const parseLabelerHeader = (
9
9
  header: string | undefined,
10
10
  ): ParsedLabelers | null => {
11
- if (!header) return null
11
+ // An empty header is valid, so we shouldn't return null
12
+ // https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
13
+ if (header === undefined) return null
12
14
  const labelerDids = new Set<string>()
13
15
  const redactDids = new Set<string>()
14
16
  const parsed = parseList(header)
@@ -265,6 +265,8 @@ export class Views {
265
265
 
266
266
  return {
267
267
  ...baseView,
268
+ pronouns: actor.profile?.pronouns,
269
+ website: this.profileWebsite(did, state),
268
270
  viewer: baseView.viewer
269
271
  ? {
270
272
  ...baseView.viewer,
@@ -444,6 +446,16 @@ export class Views {
444
446
  return undefined
445
447
  }
446
448
 
449
+ profileWebsite(did: string, state: HydrationState): string | undefined {
450
+ const actor = state.actors?.get(did)
451
+ if (!actor?.profile?.website) return
452
+ const { website } = actor.profile
453
+
454
+ // The record property accepts any URI, but we don't want
455
+ // to pass the client any schemes other than HTTPS.
456
+ return website.startsWith('https://') ? website : undefined
457
+ }
458
+
447
459
  knownFollowers(
448
460
  did: string,
449
461
  state: HydrationState,
@@ -255,6 +255,60 @@ Array [
255
255
  "muted": false,
256
256
  },
257
257
  },
258
+ Object {
259
+ "associated": Object {
260
+ "activitySubscription": Object {
261
+ "allowSubscriptions": "followers",
262
+ },
263
+ "feedgens": 0,
264
+ "labeler": false,
265
+ "lists": 0,
266
+ "starterPacks": 0,
267
+ },
268
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(7)/cids(0)@jpeg",
269
+ "createdAt": "1970-01-01T00:00:00.000Z",
270
+ "description": "It's me, eve",
271
+ "did": "user(6)",
272
+ "displayName": "eve",
273
+ "followersCount": 0,
274
+ "followsCount": 0,
275
+ "handle": "eve.test",
276
+ "indexedAt": "1970-01-01T00:00:00.000Z",
277
+ "labels": Array [],
278
+ "postsCount": 0,
279
+ "pronouns": "They/them",
280
+ "viewer": Object {
281
+ "blockedBy": false,
282
+ "muted": false,
283
+ },
284
+ },
285
+ Object {
286
+ "associated": Object {
287
+ "activitySubscription": Object {
288
+ "allowSubscriptions": "followers",
289
+ },
290
+ "feedgens": 0,
291
+ "labeler": false,
292
+ "lists": 0,
293
+ "starterPacks": 0,
294
+ },
295
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(9)/cids(0)@jpeg",
296
+ "createdAt": "1970-01-01T00:00:00.000Z",
297
+ "description": "It's me, frank",
298
+ "did": "user(8)",
299
+ "displayName": "frank",
300
+ "followersCount": 0,
301
+ "followsCount": 0,
302
+ "handle": "frank.test",
303
+ "indexedAt": "1970-01-01T00:00:00.000Z",
304
+ "labels": Array [],
305
+ "postsCount": 0,
306
+ "viewer": Object {
307
+ "blockedBy": false,
308
+ "muted": false,
309
+ },
310
+ "website": "https://frank.example.com",
311
+ },
258
312
  ]
259
313
  `;
260
314
 
@@ -16,6 +16,8 @@ describe('pds profile views', () => {
16
16
  let alice: string
17
17
  let bob: string
18
18
  let dan: string
19
+ let eve: string
20
+ let frank: string
19
21
 
20
22
  beforeAll(async () => {
21
23
  network = await TestNetwork.create({
@@ -25,10 +27,47 @@ describe('pds profile views', () => {
25
27
  pdsAgent = network.pds.getClient()
26
28
  sc = network.getSeedClient()
27
29
  await basicSeed(sc)
30
+
31
+ await sc.createAccount('eve', {
32
+ handle: 'eve.test',
33
+ email: 'eve@test.com',
34
+ password: 'eve-pass',
35
+ })
36
+ await sc.createProfile(
37
+ sc.dids.eve,
38
+ 'eve',
39
+ `It's me, eve`,
40
+ undefined,
41
+ undefined,
42
+ {
43
+ pronouns: 'They/them',
44
+ // Not allowing that to go through, even though is a valid URL.
45
+ website: 'wss://jetstream1.us-east.bsky.network',
46
+ },
47
+ )
48
+
49
+ await sc.createAccount('frank', {
50
+ handle: 'frank.test',
51
+ email: 'frank@test.com',
52
+ password: 'frank-pass',
53
+ })
54
+ await sc.createProfile(
55
+ sc.dids.frank,
56
+ 'frank',
57
+ `It's me, frank`,
58
+ undefined,
59
+ undefined,
60
+ {
61
+ website: 'https://frank.example.com',
62
+ },
63
+ )
64
+
28
65
  await network.processAll()
29
66
  alice = sc.dids.alice
30
67
  bob = sc.dids.bob
31
68
  dan = sc.dids.dan
69
+ eve = sc.dids.eve
70
+ frank = sc.dids.frank
32
71
  })
33
72
 
34
73
  afterAll(async () => {
@@ -100,6 +139,8 @@ describe('pds profile views', () => {
100
139
  'did:example:missing',
101
140
  'carol.test',
102
141
  dan,
142
+ eve,
143
+ frank,
103
144
  'missing.test',
104
145
  ],
105
146
  },
@@ -113,6 +154,8 @@ describe('pds profile views', () => {
113
154
  'bob.test',
114
155
  'carol.test',
115
156
  'dan.test',
157
+ 'eve.test',
158
+ 'frank.test',
116
159
  ])
117
160
 
118
161
  expect(forSnapshot(profiles)).toMatchSnapshot()