@atproto/bsky 0.0.214 → 0.0.215

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 (46) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.d.ts +4 -0
  3. package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.d.ts.map +1 -0
  4. package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.js +104 -0
  5. package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.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/feature-gates.d.ts +1 -0
  10. package/dist/feature-gates.d.ts.map +1 -1
  11. package/dist/feature-gates.js +1 -0
  12. package/dist/feature-gates.js.map +1 -1
  13. package/dist/lexicon/index.d.ts +4 -0
  14. package/dist/lexicon/index.d.ts.map +1 -1
  15. package/dist/lexicon/index.js +8 -0
  16. package/dist/lexicon/index.js.map +1 -1
  17. package/dist/lexicon/lexicons.d.ts +228 -10
  18. package/dist/lexicon/lexicons.d.ts.map +1 -1
  19. package/dist/lexicon/lexicons.js +116 -5
  20. package/dist/lexicon/lexicons.js.map +1 -1
  21. package/dist/lexicon/types/app/bsky/draft/defs.d.ts +1 -1
  22. package/dist/lexicon/types/app/bsky/draft/defs.d.ts.map +1 -1
  23. package/dist/lexicon/types/app/bsky/draft/defs.js.map +1 -1
  24. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedOnboardingUsers.d.ts +26 -0
  25. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedOnboardingUsers.d.ts.map +1 -0
  26. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedOnboardingUsers.js +7 -0
  27. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedOnboardingUsers.js.map +1 -0
  28. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedOnboardingUsersSkeleton.d.ts +27 -0
  29. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedOnboardingUsersSkeleton.d.ts.map +1 -0
  30. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedOnboardingUsersSkeleton.js +7 -0
  31. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedOnboardingUsersSkeleton.js.map +1 -0
  32. package/dist/lexicon/types/com/germnetwork/declaration.d.ts +7 -1
  33. package/dist/lexicon/types/com/germnetwork/declaration.d.ts.map +1 -1
  34. package/dist/lexicon/types/com/germnetwork/declaration.js.map +1 -1
  35. package/package.json +7 -7
  36. package/src/api/app/bsky/unspecced/getSuggestedOnboardingUsers.ts +176 -0
  37. package/src/api/index.ts +2 -0
  38. package/src/feature-gates.ts +1 -0
  39. package/src/lexicon/index.ts +26 -0
  40. package/src/lexicon/lexicons.ts +128 -5
  41. package/src/lexicon/types/app/bsky/draft/defs.ts +1 -1
  42. package/src/lexicon/types/app/bsky/unspecced/getSuggestedOnboardingUsers.ts +44 -0
  43. package/src/lexicon/types/app/bsky/unspecced/getSuggestedOnboardingUsersSkeleton.ts +45 -0
  44. package/src/lexicon/types/com/germnetwork/declaration.ts +7 -1
  45. package/tests/views/get-suggested-onboarding-users.test.ts +186 -0
  46. package/tsconfig.build.tsbuildinfo +1 -1
@@ -2392,9 +2392,10 @@ export const schemaDict = {
2392
2392
  properties: {
2393
2393
  text: {
2394
2394
  type: 'string',
2395
- maxLength: 3000,
2396
- maxGraphemes: 300,
2397
- description: 'The primary post content.',
2395
+ maxLength: 10000,
2396
+ maxGraphemes: 1000,
2397
+ description:
2398
+ 'The primary post content. It has a higher limit than post contents to allow storing a larger text that can later be refined into smaller posts.',
2398
2399
  },
2399
2400
  labels: {
2400
2401
  type: 'union',
@@ -8343,6 +8344,105 @@ export const schemaDict = {
8343
8344
  },
8344
8345
  },
8345
8346
  },
8347
+ AppBskyUnspeccedGetSuggestedOnboardingUsers: {
8348
+ lexicon: 1,
8349
+ id: 'app.bsky.unspecced.getSuggestedOnboardingUsers',
8350
+ defs: {
8351
+ main: {
8352
+ type: 'query',
8353
+ description: 'Get a list of suggested users for onboarding',
8354
+ parameters: {
8355
+ type: 'params',
8356
+ properties: {
8357
+ category: {
8358
+ type: 'string',
8359
+ description: 'Category of users to get suggestions for.',
8360
+ },
8361
+ limit: {
8362
+ type: 'integer',
8363
+ minimum: 1,
8364
+ maximum: 50,
8365
+ default: 25,
8366
+ },
8367
+ },
8368
+ },
8369
+ output: {
8370
+ encoding: 'application/json',
8371
+ schema: {
8372
+ type: 'object',
8373
+ required: ['actors'],
8374
+ properties: {
8375
+ actors: {
8376
+ type: 'array',
8377
+ items: {
8378
+ type: 'ref',
8379
+ ref: 'lex:app.bsky.actor.defs#profileView',
8380
+ },
8381
+ },
8382
+ recId: {
8383
+ type: 'string',
8384
+ description:
8385
+ 'Snowflake for this recommendation, use when submitting recommendation events.',
8386
+ },
8387
+ },
8388
+ },
8389
+ },
8390
+ },
8391
+ },
8392
+ },
8393
+ AppBskyUnspeccedGetSuggestedOnboardingUsersSkeleton: {
8394
+ lexicon: 1,
8395
+ id: 'app.bsky.unspecced.getSuggestedOnboardingUsersSkeleton',
8396
+ defs: {
8397
+ main: {
8398
+ type: 'query',
8399
+ description:
8400
+ 'Get a skeleton of suggested users for onboarding. Intended to be called and hydrated by app.bsky.unspecced.getSuggestedOnboardingUsers',
8401
+ parameters: {
8402
+ type: 'params',
8403
+ properties: {
8404
+ viewer: {
8405
+ type: 'string',
8406
+ format: 'did',
8407
+ description:
8408
+ 'DID of the account making the request (not included for public/unauthenticated queries).',
8409
+ },
8410
+ category: {
8411
+ type: 'string',
8412
+ description: 'Category of users to get suggestions for.',
8413
+ },
8414
+ limit: {
8415
+ type: 'integer',
8416
+ minimum: 1,
8417
+ maximum: 50,
8418
+ default: 25,
8419
+ },
8420
+ },
8421
+ },
8422
+ output: {
8423
+ encoding: 'application/json',
8424
+ schema: {
8425
+ type: 'object',
8426
+ required: ['dids'],
8427
+ properties: {
8428
+ dids: {
8429
+ type: 'array',
8430
+ items: {
8431
+ type: 'string',
8432
+ format: 'did',
8433
+ },
8434
+ },
8435
+ recId: {
8436
+ type: 'string',
8437
+ description:
8438
+ 'Snowflake for this recommendation, use when submitting recommendation events.',
8439
+ },
8440
+ },
8441
+ },
8442
+ },
8443
+ },
8444
+ },
8445
+ },
8346
8446
  AppBskyUnspeccedGetSuggestedStarterPacks: {
8347
8447
  lexicon: 1,
8348
8448
  id: 'app.bsky.unspecced.getSuggestedStarterPacks',
@@ -15346,7 +15446,7 @@ export const schemaDict = {
15346
15446
  defs: {
15347
15447
  main: {
15348
15448
  type: 'record',
15349
- description: 'A delegate messaging id',
15449
+ description: 'A declaration of a Germ Network account',
15350
15450
  key: 'literal:self',
15351
15451
  record: {
15352
15452
  type: 'object',
@@ -15354,22 +15454,33 @@ export const schemaDict = {
15354
15454
  properties: {
15355
15455
  version: {
15356
15456
  type: 'string',
15457
+ description:
15458
+ 'Semver version number, without pre-release or build information, for the format of opaque content',
15459
+ minLength: 5,
15460
+ maxLength: 14,
15357
15461
  },
15358
15462
  currentKey: {
15359
15463
  type: 'bytes',
15464
+ description:
15465
+ 'Opaque value, an ed25519 public key prefixed with a byte enum',
15360
15466
  },
15361
15467
  messageMe: {
15362
15468
  type: 'ref',
15469
+ description: 'Controls who can message this account',
15363
15470
  ref: 'lex:com.germnetwork.declaration#messageMe',
15364
15471
  },
15365
15472
  keyPackage: {
15366
15473
  type: 'bytes',
15474
+ description:
15475
+ 'Opaque value, contains MLS KeyPackage(s), and other signature data, and is signed by the currentKey',
15367
15476
  },
15368
15477
  continuityProofs: {
15369
15478
  type: 'array',
15479
+ description: 'Array of opaque values to allow for key rolling',
15370
15480
  items: {
15371
15481
  type: 'bytes',
15372
15482
  },
15483
+ maxLength: 1000,
15373
15484
  },
15374
15485
  },
15375
15486
  },
@@ -15380,11 +15491,19 @@ export const schemaDict = {
15380
15491
  properties: {
15381
15492
  messageMeUrl: {
15382
15493
  type: 'string',
15494
+ description:
15495
+ 'A URL to present to an account that does not have its own com.germnetwork.declaration record, must have an empty fragment component, where the app should fill in the fragment component with the DIDs of the two accounts who wish to message each other',
15383
15496
  format: 'uri',
15497
+ minLength: 1,
15498
+ maxLength: 2047,
15384
15499
  },
15385
15500
  showButtonTo: {
15386
15501
  type: 'string',
15387
- knownValues: ['usersIFollow', 'everyone'],
15502
+ knownValues: ['none', 'usersIFollow', 'everyone'],
15503
+ description:
15504
+ "The policy of who can message the account, this value is included in the keyPackage, but is duplicated here to allow applications to decide if they should show a 'Message on Germ' button to the viewer.",
15505
+ minLength: 1,
15506
+ maxLength: 100,
15388
15507
  },
15389
15508
  },
15390
15509
  },
@@ -15555,6 +15674,10 @@ export const ids = {
15555
15674
  AppBskyUnspeccedGetSuggestedFeeds: 'app.bsky.unspecced.getSuggestedFeeds',
15556
15675
  AppBskyUnspeccedGetSuggestedFeedsSkeleton:
15557
15676
  'app.bsky.unspecced.getSuggestedFeedsSkeleton',
15677
+ AppBskyUnspeccedGetSuggestedOnboardingUsers:
15678
+ 'app.bsky.unspecced.getSuggestedOnboardingUsers',
15679
+ AppBskyUnspeccedGetSuggestedOnboardingUsersSkeleton:
15680
+ 'app.bsky.unspecced.getSuggestedOnboardingUsersSkeleton',
15558
15681
  AppBskyUnspeccedGetSuggestedStarterPacks:
15559
15682
  'app.bsky.unspecced.getSuggestedStarterPacks',
15560
15683
  AppBskyUnspeccedGetSuggestedStarterPacksSkeleton:
@@ -75,7 +75,7 @@ export function validateDraft<V>(v: V) {
75
75
  /** One of the posts that compose a draft. */
76
76
  export interface DraftPost {
77
77
  $type?: 'app.bsky.draft.defs#draftPost'
78
- /** The primary post content. */
78
+ /** The primary post content. It has a higher limit than post contents to allow storing a larger text that can later be refined into smaller posts. */
79
79
  text: string
80
80
  labels?: $Typed<ComAtprotoLabelDefs.SelfLabels> | { $type: string }
81
81
  embedImages?: DraftEmbedImage[]
@@ -0,0 +1,44 @@
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 AppBskyActorDefs from '../actor/defs.js'
13
+
14
+ const is$typed = _is$typed,
15
+ validate = _validate
16
+ const id = 'app.bsky.unspecced.getSuggestedOnboardingUsers'
17
+
18
+ export type QueryParams = {
19
+ /** Category of users to get suggestions for. */
20
+ category?: string
21
+ limit: number
22
+ }
23
+ export type InputSchema = undefined
24
+
25
+ export interface OutputSchema {
26
+ actors: AppBskyActorDefs.ProfileView[]
27
+ /** Snowflake for this recommendation, use when submitting recommendation events. */
28
+ recId?: string
29
+ }
30
+
31
+ export type HandlerInput = void
32
+
33
+ export interface HandlerSuccess {
34
+ encoding: 'application/json'
35
+ body: OutputSchema
36
+ headers?: { [key: string]: string }
37
+ }
38
+
39
+ export interface HandlerError {
40
+ status: number
41
+ message?: string
42
+ }
43
+
44
+ export type HandlerOutput = HandlerError | HandlerSuccess
@@ -0,0 +1,45 @@
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.getSuggestedOnboardingUsersSkeleton'
16
+
17
+ export type QueryParams = {
18
+ /** DID of the account making the request (not included for public/unauthenticated queries). */
19
+ viewer?: string
20
+ /** Category of users to get suggestions for. */
21
+ category?: string
22
+ limit: number
23
+ }
24
+ export type InputSchema = undefined
25
+
26
+ export interface OutputSchema {
27
+ dids: string[]
28
+ /** Snowflake for this recommendation, use when submitting recommendation events. */
29
+ recId?: string
30
+ }
31
+
32
+ export type HandlerInput = void
33
+
34
+ export interface HandlerSuccess {
35
+ encoding: 'application/json'
36
+ body: OutputSchema
37
+ headers?: { [key: string]: string }
38
+ }
39
+
40
+ export interface HandlerError {
41
+ status: number
42
+ message?: string
43
+ }
44
+
45
+ export type HandlerOutput = HandlerError | HandlerSuccess
@@ -12,10 +12,14 @@ const id = 'com.germnetwork.declaration'
12
12
 
13
13
  export interface Main {
14
14
  $type: 'com.germnetwork.declaration'
15
+ /** Semver version number, without pre-release or build information, for the format of opaque content */
15
16
  version: string
17
+ /** Opaque value, an ed25519 public key prefixed with a byte enum */
16
18
  currentKey: Uint8Array
17
19
  messageMe?: MessageMe
20
+ /** Opaque value, contains MLS KeyPackage(s), and other signature data, and is signed by the currentKey */
18
21
  keyPackage?: Uint8Array
22
+ /** Array of opaque values to allow for key rolling */
19
23
  continuityProofs?: Uint8Array[]
20
24
  [k: string]: unknown
21
25
  }
@@ -38,8 +42,10 @@ export {
38
42
 
39
43
  export interface MessageMe {
40
44
  $type?: 'com.germnetwork.declaration#messageMe'
45
+ /** A URL to present to an account that does not have its own com.germnetwork.declaration record, must have an empty fragment component, where the app should fill in the fragment component with the DIDs of the two accounts who wish to message each other */
41
46
  messageMeUrl: string
42
- showButtonTo: 'usersIFollow' | 'everyone' | (string & {})
47
+ /** The policy of who can message the account, this value is included in the keyPackage, but is duplicated here to allow applications to decide if they should show a 'Message on Germ' button to the viewer. */
48
+ showButtonTo: 'none' | 'usersIFollow' | 'everyone' | (string & {})
43
49
  }
44
50
 
45
51
  const hashMessageMe = 'messageMe'
@@ -0,0 +1,186 @@
1
+ import { once } from 'node:events'
2
+ import { Server, createServer } from 'node:http'
3
+ import { AddressInfo } from 'node:net'
4
+ import express, { Application } from 'express'
5
+ import AtpAgent from '@atproto/api'
6
+ import { SeedClient, TestNetwork } from '@atproto/dev-env'
7
+ import { ids } from '../../src/lexicon/lexicons'
8
+ import { OutputSchema } from '../../src/lexicon/types/app/bsky/unspecced/getSuggestedOnboardingUsersSkeleton'
9
+
10
+ type User = {
11
+ id: string
12
+ did: string
13
+ email: string
14
+ handle: string
15
+ password: string
16
+ displayName: string
17
+ description: string
18
+ selfLabels: undefined
19
+ }
20
+
21
+ function createUser(name: string): User {
22
+ return {
23
+ id: name,
24
+ // @ts-ignore overwritten below
25
+ did: undefined,
26
+ email: `${name}@test.com`,
27
+ handle: `${name}.test`,
28
+ password: `${name}-pass`,
29
+ displayName: name,
30
+ description: `hi im ${name}`,
31
+ selfLabels: undefined,
32
+ }
33
+ }
34
+
35
+ const users = {
36
+ suggestedUser: createUser('suggested-user'),
37
+ viewer: createUser('viewer'),
38
+ viewerBlocker: createUser('viewer-blocker'),
39
+ followedUser: createUser('followed-user'),
40
+ }
41
+
42
+ type Users = typeof users
43
+
44
+ async function seed(sc: SeedClient) {
45
+ const u = structuredClone(users)
46
+
47
+ for (const [key, user] of Object.entries(u)) {
48
+ await sc.createAccount(key, user)
49
+ u[key].did = sc.dids[key]
50
+ }
51
+
52
+ await sc.block(u.viewerBlocker.did, u.suggestedUser.did)
53
+ await sc.follow(u.viewer.did, u.followedUser.did)
54
+
55
+ await sc.network.processAll()
56
+
57
+ return { users: u }
58
+ }
59
+
60
+ describe('getSuggestedOnboardingUsers', () => {
61
+ let network: TestNetwork
62
+ let agent: AtpAgent
63
+ let sc: SeedClient
64
+ let seededUsers: Users
65
+ let mockServer: MockServer
66
+
67
+ beforeAll(async () => {
68
+ mockServer = new MockServer()
69
+ await mockServer.listen()
70
+
71
+ network = await TestNetwork.create({
72
+ dbPostgresSchema: 'bsky_tests_get_suggested_onboarding_users',
73
+ bsky: {
74
+ topicsUrl: mockServer.url,
75
+ topicsApiKey: 'test',
76
+ },
77
+ })
78
+ agent = network.bsky.getClient()
79
+ sc = network.getSeedClient()
80
+
81
+ const result = await seed(sc)
82
+ seededUsers = result.users
83
+
84
+ await network.processAll()
85
+ })
86
+
87
+ afterAll(async () => {
88
+ await network.close()
89
+ await mockServer.stop()
90
+ })
91
+
92
+ describe(`basic handling`, () => {
93
+ beforeAll(() => {
94
+ mockServer.mockedDids.set('suggestedUser', seededUsers.suggestedUser.did)
95
+ })
96
+
97
+ afterAll(() => {
98
+ mockServer.mockedDids.delete('suggestedUser')
99
+ })
100
+
101
+ it(`returns users for non-blocking viewer`, async () => {
102
+ const { data } =
103
+ await agent.app.bsky.unspecced.getSuggestedOnboardingUsers(undefined, {
104
+ headers: await network.serviceHeaders(
105
+ seededUsers.viewer.did,
106
+ ids.AppBskyUnspeccedGetSuggestedOnboardingUsers,
107
+ ),
108
+ })
109
+ const actor = data.actors.find(
110
+ (a) => a.did === seededUsers.suggestedUser.did,
111
+ )
112
+ expect(actor).toBeDefined()
113
+ })
114
+
115
+ it(`does not return user if blocked by viewer`, async () => {
116
+ const { data } =
117
+ await agent.app.bsky.unspecced.getSuggestedOnboardingUsers(undefined, {
118
+ headers: await network.serviceHeaders(
119
+ seededUsers.viewerBlocker.did,
120
+ ids.AppBskyUnspeccedGetSuggestedOnboardingUsers,
121
+ ),
122
+ })
123
+ const actor = data.actors.find(
124
+ (a) => a.did === seededUsers.suggestedUser.did,
125
+ )
126
+ expect(actor).not.toBeDefined()
127
+ })
128
+
129
+ it(`does not return users that viewer already follows`, async () => {
130
+ mockServer.mockedDids.set('followedUser', seededUsers.followedUser.did)
131
+ const { data } =
132
+ await agent.app.bsky.unspecced.getSuggestedOnboardingUsers(undefined, {
133
+ headers: await network.serviceHeaders(
134
+ seededUsers.viewer.did,
135
+ ids.AppBskyUnspeccedGetSuggestedOnboardingUsers,
136
+ ),
137
+ })
138
+ const actor = data.actors.find(
139
+ (a) => a.did === seededUsers.followedUser.did,
140
+ )
141
+ expect(actor).not.toBeDefined()
142
+ mockServer.mockedDids.delete('followedUser')
143
+ })
144
+ })
145
+ })
146
+
147
+ class MockServer {
148
+ app: Application
149
+ server: Server
150
+
151
+ mockedDids = new Map<string, string>()
152
+
153
+ constructor() {
154
+ this.app = this.createApp()
155
+ this.server = createServer(this.app)
156
+ }
157
+
158
+ async listen(port?: number) {
159
+ this.server.listen(port)
160
+ await once(this.server, 'listening')
161
+ }
162
+
163
+ async stop() {
164
+ this.server.close()
165
+ await once(this.server, 'close')
166
+ }
167
+
168
+ get url() {
169
+ const address = this.server.address() as AddressInfo
170
+ return `http://localhost:${address.port}`
171
+ }
172
+
173
+ private createApp() {
174
+ const app = express()
175
+ app.get(
176
+ '/xrpc/app.bsky.unspecced.getSuggestedUsersSkeleton',
177
+ (req, res) => {
178
+ const skeleton: OutputSchema = {
179
+ dids: Array.from(this.mockedDids.values()),
180
+ }
181
+ return res.json(skeleton)
182
+ },
183
+ )
184
+ return app
185
+ }
186
+ }