@atproto/bsky 0.0.173 → 0.0.174
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.
- package/CHANGELOG.md +9 -0
- package/dist/api/app/bsky/unspecced/checkHandleAvailability.d.ts +4 -0
- package/dist/api/app/bsky/unspecced/checkHandleAvailability.d.ts.map +1 -0
- package/dist/api/app/bsky/unspecced/checkHandleAvailability.js +238 -0
- package/dist/api/app/bsky/unspecced/checkHandleAvailability.js.map +1 -0
- package/dist/api/app/bsky/unspecced/initAgeAssurance.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/initAgeAssurance.js +20 -0
- package/dist/api/app/bsky/unspecced/initAgeAssurance.js.map +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -0
- package/dist/api/index.js.map +1 -1
- package/dist/lexicon/index.d.ts +4 -2
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +8 -4
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +258 -82
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +137 -42
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/checkHandleAvailability.d.ts +58 -0
- package/dist/lexicon/types/app/bsky/unspecced/checkHandleAvailability.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/unspecced/checkHandleAvailability.js +34 -0
- package/dist/lexicon/types/app/bsky/unspecced/checkHandleAvailability.js.map +1 -0
- package/dist/lexicon/types/app/bsky/unspecced/initAgeAssurance.d.ts +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/initAgeAssurance.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/api/app/bsky/unspecced/checkHandleAvailability.ts +291 -0
- package/src/api/app/bsky/unspecced/initAgeAssurance.ts +32 -0
- package/src/api/index.ts +2 -0
- package/src/lexicon/index.ts +24 -11
- package/src/lexicon/lexicons.ts +146 -43
- package/src/lexicon/types/app/bsky/unspecced/checkHandleAvailability.ts +99 -0
- package/src/lexicon/types/app/bsky/unspecced/initAgeAssurance.ts +1 -1
- package/tests/views/age-assurance.test.ts +44 -0
- package/tests/views/handle-availability.test.ts +294 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -7282,6 +7282,48 @@ export const schemaDict = {
|
|
|
7282
7282
|
},
|
|
7283
7283
|
},
|
|
7284
7284
|
},
|
|
7285
|
+
AppBskyFeedGetPosts: {
|
|
7286
|
+
lexicon: 1,
|
|
7287
|
+
id: 'app.bsky.feed.getPosts',
|
|
7288
|
+
defs: {
|
|
7289
|
+
main: {
|
|
7290
|
+
type: 'query',
|
|
7291
|
+
description:
|
|
7292
|
+
"Gets post views for a specified list of posts (by AT-URI). This is sometimes referred to as 'hydrating' a 'feed skeleton'.",
|
|
7293
|
+
parameters: {
|
|
7294
|
+
type: 'params',
|
|
7295
|
+
required: ['uris'],
|
|
7296
|
+
properties: {
|
|
7297
|
+
uris: {
|
|
7298
|
+
type: 'array',
|
|
7299
|
+
description: 'List of post AT-URIs to return hydrated views for.',
|
|
7300
|
+
items: {
|
|
7301
|
+
type: 'string',
|
|
7302
|
+
format: 'at-uri',
|
|
7303
|
+
},
|
|
7304
|
+
maxLength: 25,
|
|
7305
|
+
},
|
|
7306
|
+
},
|
|
7307
|
+
},
|
|
7308
|
+
output: {
|
|
7309
|
+
encoding: 'application/json',
|
|
7310
|
+
schema: {
|
|
7311
|
+
type: 'object',
|
|
7312
|
+
required: ['posts'],
|
|
7313
|
+
properties: {
|
|
7314
|
+
posts: {
|
|
7315
|
+
type: 'array',
|
|
7316
|
+
items: {
|
|
7317
|
+
type: 'ref',
|
|
7318
|
+
ref: 'lex:app.bsky.feed.defs#postView',
|
|
7319
|
+
},
|
|
7320
|
+
},
|
|
7321
|
+
},
|
|
7322
|
+
},
|
|
7323
|
+
},
|
|
7324
|
+
},
|
|
7325
|
+
},
|
|
7326
|
+
},
|
|
7285
7327
|
AppBskyFeedGetPostThread: {
|
|
7286
7328
|
lexicon: 1,
|
|
7287
7329
|
id: 'app.bsky.feed.getPostThread',
|
|
@@ -7346,48 +7388,6 @@ export const schemaDict = {
|
|
|
7346
7388
|
},
|
|
7347
7389
|
},
|
|
7348
7390
|
},
|
|
7349
|
-
AppBskyFeedGetPosts: {
|
|
7350
|
-
lexicon: 1,
|
|
7351
|
-
id: 'app.bsky.feed.getPosts',
|
|
7352
|
-
defs: {
|
|
7353
|
-
main: {
|
|
7354
|
-
type: 'query',
|
|
7355
|
-
description:
|
|
7356
|
-
"Gets post views for a specified list of posts (by AT-URI). This is sometimes referred to as 'hydrating' a 'feed skeleton'.",
|
|
7357
|
-
parameters: {
|
|
7358
|
-
type: 'params',
|
|
7359
|
-
required: ['uris'],
|
|
7360
|
-
properties: {
|
|
7361
|
-
uris: {
|
|
7362
|
-
type: 'array',
|
|
7363
|
-
description: 'List of post AT-URIs to return hydrated views for.',
|
|
7364
|
-
items: {
|
|
7365
|
-
type: 'string',
|
|
7366
|
-
format: 'at-uri',
|
|
7367
|
-
},
|
|
7368
|
-
maxLength: 25,
|
|
7369
|
-
},
|
|
7370
|
-
},
|
|
7371
|
-
},
|
|
7372
|
-
output: {
|
|
7373
|
-
encoding: 'application/json',
|
|
7374
|
-
schema: {
|
|
7375
|
-
type: 'object',
|
|
7376
|
-
required: ['posts'],
|
|
7377
|
-
properties: {
|
|
7378
|
-
posts: {
|
|
7379
|
-
type: 'array',
|
|
7380
|
-
items: {
|
|
7381
|
-
type: 'ref',
|
|
7382
|
-
ref: 'lex:app.bsky.feed.defs#postView',
|
|
7383
|
-
},
|
|
7384
|
-
},
|
|
7385
|
-
},
|
|
7386
|
-
},
|
|
7387
|
-
},
|
|
7388
|
-
},
|
|
7389
|
-
},
|
|
7390
|
-
},
|
|
7391
7391
|
AppBskyFeedGetQuotes: {
|
|
7392
7392
|
lexicon: 1,
|
|
7393
7393
|
id: 'app.bsky.feed.getQuotes',
|
|
@@ -10511,6 +10511,104 @@ export const schemaDict = {
|
|
|
10511
10511
|
},
|
|
10512
10512
|
},
|
|
10513
10513
|
},
|
|
10514
|
+
AppBskyUnspeccedCheckHandleAvailability: {
|
|
10515
|
+
lexicon: 1,
|
|
10516
|
+
id: 'app.bsky.unspecced.checkHandleAvailability',
|
|
10517
|
+
defs: {
|
|
10518
|
+
main: {
|
|
10519
|
+
type: 'query',
|
|
10520
|
+
description:
|
|
10521
|
+
'Checks whether the provided handle is available. If the handle is not available, available suggestions will be returned. Optional inputs will be used to generate suggestions.',
|
|
10522
|
+
parameters: {
|
|
10523
|
+
type: 'params',
|
|
10524
|
+
required: ['handle'],
|
|
10525
|
+
properties: {
|
|
10526
|
+
handle: {
|
|
10527
|
+
type: 'string',
|
|
10528
|
+
format: 'handle',
|
|
10529
|
+
description:
|
|
10530
|
+
'Tentative handle. Will be checked for availability or used to build handle suggestions.',
|
|
10531
|
+
},
|
|
10532
|
+
email: {
|
|
10533
|
+
type: 'string',
|
|
10534
|
+
description:
|
|
10535
|
+
'User-provided email. Might be used to build handle suggestions.',
|
|
10536
|
+
},
|
|
10537
|
+
birthDate: {
|
|
10538
|
+
type: 'string',
|
|
10539
|
+
format: 'datetime',
|
|
10540
|
+
description:
|
|
10541
|
+
'User-provided birth date. Might be used to build handle suggestions.',
|
|
10542
|
+
},
|
|
10543
|
+
},
|
|
10544
|
+
},
|
|
10545
|
+
output: {
|
|
10546
|
+
encoding: 'application/json',
|
|
10547
|
+
schema: {
|
|
10548
|
+
type: 'object',
|
|
10549
|
+
required: ['handle', 'result'],
|
|
10550
|
+
properties: {
|
|
10551
|
+
handle: {
|
|
10552
|
+
type: 'string',
|
|
10553
|
+
format: 'handle',
|
|
10554
|
+
description: 'Echo of the input handle.',
|
|
10555
|
+
},
|
|
10556
|
+
result: {
|
|
10557
|
+
type: 'union',
|
|
10558
|
+
refs: [
|
|
10559
|
+
'lex:app.bsky.unspecced.checkHandleAvailability#resultAvailable',
|
|
10560
|
+
'lex:app.bsky.unspecced.checkHandleAvailability#resultUnavailable',
|
|
10561
|
+
],
|
|
10562
|
+
},
|
|
10563
|
+
},
|
|
10564
|
+
},
|
|
10565
|
+
},
|
|
10566
|
+
errors: [
|
|
10567
|
+
{
|
|
10568
|
+
name: 'InvalidEmail',
|
|
10569
|
+
description: 'An invalid email was provided.',
|
|
10570
|
+
},
|
|
10571
|
+
],
|
|
10572
|
+
},
|
|
10573
|
+
resultAvailable: {
|
|
10574
|
+
type: 'object',
|
|
10575
|
+
description: 'Indicates the provided handle is available.',
|
|
10576
|
+
properties: {},
|
|
10577
|
+
},
|
|
10578
|
+
resultUnavailable: {
|
|
10579
|
+
type: 'object',
|
|
10580
|
+
description:
|
|
10581
|
+
'Indicates the provided handle is unavailable and gives suggestions of available handles.',
|
|
10582
|
+
required: ['suggestions'],
|
|
10583
|
+
properties: {
|
|
10584
|
+
suggestions: {
|
|
10585
|
+
type: 'array',
|
|
10586
|
+
description:
|
|
10587
|
+
'List of suggested handles based on the provided inputs.',
|
|
10588
|
+
items: {
|
|
10589
|
+
type: 'ref',
|
|
10590
|
+
ref: 'lex:app.bsky.unspecced.checkHandleAvailability#suggestion',
|
|
10591
|
+
},
|
|
10592
|
+
},
|
|
10593
|
+
},
|
|
10594
|
+
},
|
|
10595
|
+
suggestion: {
|
|
10596
|
+
type: 'object',
|
|
10597
|
+
required: ['handle', 'method'],
|
|
10598
|
+
properties: {
|
|
10599
|
+
handle: {
|
|
10600
|
+
type: 'string',
|
|
10601
|
+
format: 'handle',
|
|
10602
|
+
},
|
|
10603
|
+
method: {
|
|
10604
|
+
type: 'string',
|
|
10605
|
+
description:
|
|
10606
|
+
'Method used to build this suggestion. Should be considered opaque to clients. Can be used for metrics.',
|
|
10607
|
+
},
|
|
10608
|
+
},
|
|
10609
|
+
},
|
|
10610
|
+
},
|
|
10611
|
+
},
|
|
10514
10612
|
AppBskyUnspeccedDefs: {
|
|
10515
10613
|
lexicon: 1,
|
|
10516
10614
|
id: 'app.bsky.unspecced.defs',
|
|
@@ -11596,6 +11694,9 @@ export const schemaDict = {
|
|
|
11596
11694
|
{
|
|
11597
11695
|
name: 'DidTooLong',
|
|
11598
11696
|
},
|
|
11697
|
+
{
|
|
11698
|
+
name: 'InvalidInitiation',
|
|
11699
|
+
},
|
|
11599
11700
|
],
|
|
11600
11701
|
},
|
|
11601
11702
|
},
|
|
@@ -13468,8 +13569,8 @@ export const ids = {
|
|
|
13468
13569
|
AppBskyFeedGetFeedSkeleton: 'app.bsky.feed.getFeedSkeleton',
|
|
13469
13570
|
AppBskyFeedGetLikes: 'app.bsky.feed.getLikes',
|
|
13470
13571
|
AppBskyFeedGetListFeed: 'app.bsky.feed.getListFeed',
|
|
13471
|
-
AppBskyFeedGetPostThread: 'app.bsky.feed.getPostThread',
|
|
13472
13572
|
AppBskyFeedGetPosts: 'app.bsky.feed.getPosts',
|
|
13573
|
+
AppBskyFeedGetPostThread: 'app.bsky.feed.getPostThread',
|
|
13473
13574
|
AppBskyFeedGetQuotes: 'app.bsky.feed.getQuotes',
|
|
13474
13575
|
AppBskyFeedGetRepostedBy: 'app.bsky.feed.getRepostedBy',
|
|
13475
13576
|
AppBskyFeedGetSuggestedFeeds: 'app.bsky.feed.getSuggestedFeeds',
|
|
@@ -13530,6 +13631,8 @@ export const ids = {
|
|
|
13530
13631
|
AppBskyNotificationUnregisterPush: 'app.bsky.notification.unregisterPush',
|
|
13531
13632
|
AppBskyNotificationUpdateSeen: 'app.bsky.notification.updateSeen',
|
|
13532
13633
|
AppBskyRichtextFacet: 'app.bsky.richtext.facet',
|
|
13634
|
+
AppBskyUnspeccedCheckHandleAvailability:
|
|
13635
|
+
'app.bsky.unspecced.checkHandleAvailability',
|
|
13533
13636
|
AppBskyUnspeccedDefs: 'app.bsky.unspecced.defs',
|
|
13534
13637
|
AppBskyUnspeccedGetAgeAssuranceState:
|
|
13535
13638
|
'app.bsky.unspecced.getAgeAssuranceState',
|
|
@@ -0,0 +1,99 @@
|
|
|
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.checkHandleAvailability'
|
|
16
|
+
|
|
17
|
+
export type QueryParams = {
|
|
18
|
+
/** Tentative handle. Will be checked for availability or used to build handle suggestions. */
|
|
19
|
+
handle: string
|
|
20
|
+
/** User-provided email. Might be used to build handle suggestions. */
|
|
21
|
+
email?: string
|
|
22
|
+
/** User-provided birth date. Might be used to build handle suggestions. */
|
|
23
|
+
birthDate?: string
|
|
24
|
+
}
|
|
25
|
+
export type InputSchema = undefined
|
|
26
|
+
|
|
27
|
+
export interface OutputSchema {
|
|
28
|
+
/** Echo of the input handle. */
|
|
29
|
+
handle: string
|
|
30
|
+
result:
|
|
31
|
+
| $Typed<ResultAvailable>
|
|
32
|
+
| $Typed<ResultUnavailable>
|
|
33
|
+
| { $type: string }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type HandlerInput = void
|
|
37
|
+
|
|
38
|
+
export interface HandlerSuccess {
|
|
39
|
+
encoding: 'application/json'
|
|
40
|
+
body: OutputSchema
|
|
41
|
+
headers?: { [key: string]: string }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface HandlerError {
|
|
45
|
+
status: number
|
|
46
|
+
message?: string
|
|
47
|
+
error?: 'InvalidEmail'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type HandlerOutput = HandlerError | HandlerSuccess
|
|
51
|
+
|
|
52
|
+
/** Indicates the provided handle is available. */
|
|
53
|
+
export interface ResultAvailable {
|
|
54
|
+
$type?: 'app.bsky.unspecced.checkHandleAvailability#resultAvailable'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const hashResultAvailable = 'resultAvailable'
|
|
58
|
+
|
|
59
|
+
export function isResultAvailable<V>(v: V) {
|
|
60
|
+
return is$typed(v, id, hashResultAvailable)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function validateResultAvailable<V>(v: V) {
|
|
64
|
+
return validate<ResultAvailable & V>(v, id, hashResultAvailable)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Indicates the provided handle is unavailable and gives suggestions of available handles. */
|
|
68
|
+
export interface ResultUnavailable {
|
|
69
|
+
$type?: 'app.bsky.unspecced.checkHandleAvailability#resultUnavailable'
|
|
70
|
+
/** List of suggested handles based on the provided inputs. */
|
|
71
|
+
suggestions: Suggestion[]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const hashResultUnavailable = 'resultUnavailable'
|
|
75
|
+
|
|
76
|
+
export function isResultUnavailable<V>(v: V) {
|
|
77
|
+
return is$typed(v, id, hashResultUnavailable)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function validateResultUnavailable<V>(v: V) {
|
|
81
|
+
return validate<ResultUnavailable & V>(v, id, hashResultUnavailable)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface Suggestion {
|
|
85
|
+
$type?: 'app.bsky.unspecced.checkHandleAvailability#suggestion'
|
|
86
|
+
handle: string
|
|
87
|
+
/** Method used to build this suggestion. Should be considered opaque to clients. Can be used for metrics. */
|
|
88
|
+
method: string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const hashSuggestion = 'suggestion'
|
|
92
|
+
|
|
93
|
+
export function isSuggestion<V>(v: V) {
|
|
94
|
+
return is$typed(v, id, hashSuggestion)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function validateSuggestion<V>(v: V) {
|
|
98
|
+
return validate<Suggestion & V>(v, id, hashSuggestion)
|
|
99
|
+
}
|
|
@@ -42,7 +42,7 @@ export interface HandlerSuccess {
|
|
|
42
42
|
export interface HandlerError {
|
|
43
43
|
status: number
|
|
44
44
|
message?: string
|
|
45
|
-
error?: 'InvalidEmail' | 'DidTooLong'
|
|
45
|
+
error?: 'InvalidEmail' | 'DidTooLong' | 'InvalidInitiation'
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export type HandlerOutput = HandlerError | HandlerSuccess
|
|
@@ -189,6 +189,50 @@ describe('age assurance views', () => {
|
|
|
189
189
|
)
|
|
190
190
|
})
|
|
191
191
|
|
|
192
|
+
it('ensures user cannot re-init flow from terminal state', async () => {
|
|
193
|
+
const actor = sc.dids.bob
|
|
194
|
+
const state0 = await getAgeAssurance(actor)
|
|
195
|
+
expect(state0).toStrictEqual({
|
|
196
|
+
status: 'unknown',
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
const init1 = await initAgeAssurance(actor)
|
|
200
|
+
expect(init1).toStrictEqual({
|
|
201
|
+
status: 'pending',
|
|
202
|
+
lastInitiatedAt: expect.any(String),
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
const init2 = await initAgeAssurance(actor)
|
|
206
|
+
expect(init2).toStrictEqual({
|
|
207
|
+
status: 'pending',
|
|
208
|
+
lastInitiatedAt: expect.any(String),
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Can re-init flow if the state is pending.
|
|
213
|
+
*/
|
|
214
|
+
expect(sendEmailMock).toHaveBeenCalledTimes(2)
|
|
215
|
+
|
|
216
|
+
const externalPayload: KwsExternalPayload = {
|
|
217
|
+
actorDid: actor,
|
|
218
|
+
attemptId,
|
|
219
|
+
}
|
|
220
|
+
const status = { verified: true }
|
|
221
|
+
await kwsServer.callVerificationResponse(network.bsky.url, {
|
|
222
|
+
externalPayload,
|
|
223
|
+
status,
|
|
224
|
+
})
|
|
225
|
+
const finalizedState = await getAgeAssurance(actor)
|
|
226
|
+
expect(finalizedState).toStrictEqual({
|
|
227
|
+
status: 'assured',
|
|
228
|
+
lastInitiatedAt: expect.any(String),
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
await expect(initAgeAssurance(actor)).rejects.toThrowError(
|
|
232
|
+
`Cannot initiate age assurance flow from current state: assured`,
|
|
233
|
+
)
|
|
234
|
+
})
|
|
235
|
+
|
|
192
236
|
describe('verification response flow', () => {
|
|
193
237
|
it('performs the AA flow', async () => {
|
|
194
238
|
const state0 = await getAgeAssurance(actorDid)
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
2
|
+
import {
|
|
3
|
+
$Typed,
|
|
4
|
+
AppBskyUnspeccedCheckHandleAvailability,
|
|
5
|
+
AtpAgent,
|
|
6
|
+
} from '@atproto/api'
|
|
7
|
+
import { InvalidEmailError } from '@atproto/api/dist/client/types/app/bsky/unspecced/checkHandleAvailability'
|
|
8
|
+
import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
|
|
9
|
+
import {
|
|
10
|
+
OutputSchema,
|
|
11
|
+
ResultAvailable,
|
|
12
|
+
ResultUnavailable,
|
|
13
|
+
} from '../../src/lexicon/types/app/bsky/unspecced/checkHandleAvailability'
|
|
14
|
+
|
|
15
|
+
describe('handle availability', () => {
|
|
16
|
+
let network: TestNetwork
|
|
17
|
+
let agent: AtpAgent
|
|
18
|
+
let sc: SeedClient
|
|
19
|
+
|
|
20
|
+
let did: string
|
|
21
|
+
let handle: string
|
|
22
|
+
let handleSubdomain: string
|
|
23
|
+
const birthDate = '1980-09-11T18:05:42.556Z'
|
|
24
|
+
|
|
25
|
+
beforeAll(async () => {
|
|
26
|
+
network = await TestNetwork.create({
|
|
27
|
+
dbPostgresSchema: 'bsky_handle_availability',
|
|
28
|
+
})
|
|
29
|
+
agent = network.bsky.getClient()
|
|
30
|
+
sc = network.getSeedClient()
|
|
31
|
+
await basicSeed(sc)
|
|
32
|
+
await network.processAll()
|
|
33
|
+
did = sc.dids.alice
|
|
34
|
+
handle = sc.accounts[did].handle
|
|
35
|
+
handleSubdomain = handle.split('.')[0]
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
jest.resetAllMocks()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
afterAll(async () => {
|
|
43
|
+
await network.close()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
describe('validation', () => {
|
|
47
|
+
it('throws if email passed is invalid', async () => {
|
|
48
|
+
await expect(
|
|
49
|
+
agent.app.bsky.unspecced.checkHandleAvailability({
|
|
50
|
+
handle,
|
|
51
|
+
email: 'not-an-email',
|
|
52
|
+
}),
|
|
53
|
+
).rejects.toThrow(InvalidEmailError)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it(`returns unavailable with empty suggestions input is already a slur`, async () => {
|
|
57
|
+
const {
|
|
58
|
+
data: { result },
|
|
59
|
+
} = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
60
|
+
handle: 'shit.test',
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
assertUnavailable(result)
|
|
64
|
+
expect(result.suggestions).toStrictEqual([])
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('available handle', () => {
|
|
69
|
+
it('returns available when trying a non-existing handle', async () => {
|
|
70
|
+
const handle = 'a5cc0a41-f9a3-4351-b9ec-1462f255fb0a.test'
|
|
71
|
+
const { data } = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
72
|
+
handle,
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
expect(data.handle).toBe(handle)
|
|
76
|
+
assertAvailable(data.result)
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
describe('unavailable handle', () => {
|
|
81
|
+
it('returns unavailable when trying an existing handle', async () => {
|
|
82
|
+
const { data } = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
83
|
+
handle,
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
expect(data.handle).toBe(handle)
|
|
87
|
+
assertUnavailable(data.result)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
describe('suggestions', () => {
|
|
91
|
+
it(`returns empty list if can't create suggestions`, async () => {
|
|
92
|
+
// This handle has the maximum allowed length. Suggestions are additive,
|
|
93
|
+
// so no valid suggestion can be made by adding characters.
|
|
94
|
+
const longestAllowedHandle = 'abcdefghijklmnopqr.test'
|
|
95
|
+
const userMaxLength = {
|
|
96
|
+
email: 'usermaxlength@mail.com',
|
|
97
|
+
handle: longestAllowedHandle,
|
|
98
|
+
password: 'hunter2',
|
|
99
|
+
}
|
|
100
|
+
await sc.createAccount('userMaxLength', userMaxLength)
|
|
101
|
+
await network.processAll()
|
|
102
|
+
const { data } = await agent.app.bsky.unspecced.checkHandleAvailability(
|
|
103
|
+
{
|
|
104
|
+
handle: longestAllowedHandle,
|
|
105
|
+
},
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
expect(data.handle).toBe(longestAllowedHandle)
|
|
109
|
+
assertUnavailable(data.result)
|
|
110
|
+
expect(data.result.suggestions).toHaveLength(0)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('suggests appending YOB to tentative handle', async () => {
|
|
114
|
+
const {
|
|
115
|
+
data: { result },
|
|
116
|
+
} = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
117
|
+
handle,
|
|
118
|
+
birthDate,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
assertUnavailable(result)
|
|
122
|
+
const suggestion = result.suggestions.find(
|
|
123
|
+
(s) => s.method === 'handle_yob',
|
|
124
|
+
)
|
|
125
|
+
expect(suggestion?.handle).toBe(`${handleSubdomain}80.test`)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('suggests appending YOB to tentative handle, with hyphen if label ends with digits', async () => {
|
|
129
|
+
const user1 = {
|
|
130
|
+
email: 'user1@mail.com',
|
|
131
|
+
handle: 'user1.test',
|
|
132
|
+
password: 'hunter2',
|
|
133
|
+
}
|
|
134
|
+
await sc.createAccount('user1', user1)
|
|
135
|
+
await network.processAll()
|
|
136
|
+
|
|
137
|
+
const {
|
|
138
|
+
data: { result },
|
|
139
|
+
} = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
140
|
+
handle: user1.handle,
|
|
141
|
+
birthDate,
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
assertUnavailable(result)
|
|
145
|
+
const suggestion = result.suggestions.find(
|
|
146
|
+
(s) => s.method === 'handle_yob',
|
|
147
|
+
)
|
|
148
|
+
expect(suggestion?.handle).toBe('user1-80.test')
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('suggests using email', async () => {
|
|
152
|
+
const {
|
|
153
|
+
data: { result },
|
|
154
|
+
} = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
155
|
+
handle,
|
|
156
|
+
email: 'email@mail.com',
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
assertUnavailable(result)
|
|
160
|
+
const suggestion = result.suggestions.find((s) => s.method === 'email')
|
|
161
|
+
expect(suggestion?.handle).toBe('email.test')
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('suggests user email with and without YOB', async () => {
|
|
165
|
+
const {
|
|
166
|
+
data: { result },
|
|
167
|
+
} = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
168
|
+
handle,
|
|
169
|
+
email: 'email@mail.com',
|
|
170
|
+
birthDate,
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
assertUnavailable(result)
|
|
174
|
+
|
|
175
|
+
const suggestion0 = result.suggestions.find((s) => s.method === 'email')
|
|
176
|
+
expect(suggestion0?.handle).toBe('email.test')
|
|
177
|
+
|
|
178
|
+
const suggestion1 = result.suggestions.find(
|
|
179
|
+
(s) => s.method === 'email_yob',
|
|
180
|
+
)
|
|
181
|
+
expect(suggestion1?.handle).toBe('email80.test')
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('does not suggest email if it is unavailable as handle', async () => {
|
|
185
|
+
const user2 = {
|
|
186
|
+
email: 'user2@mail.com',
|
|
187
|
+
handle: 'some-name.test', // NOTE: this handle is taken.
|
|
188
|
+
password: 'hunter2',
|
|
189
|
+
}
|
|
190
|
+
await sc.createAccount('user2', user2)
|
|
191
|
+
await network.processAll()
|
|
192
|
+
|
|
193
|
+
const {
|
|
194
|
+
data: { result },
|
|
195
|
+
} = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
196
|
+
handle,
|
|
197
|
+
email: 'some.name@mail.com', // NOTE: would suggest 'some-name.test' from the email, but it is taken.
|
|
198
|
+
birthDate,
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
assertUnavailable(result)
|
|
202
|
+
|
|
203
|
+
const suggestion0 = result.suggestions.find((s) => s.method === 'email')
|
|
204
|
+
expect(suggestion0).toBeUndefined()
|
|
205
|
+
|
|
206
|
+
const suggestion1 = result.suggestions.find(
|
|
207
|
+
(s) => s.method === 'email_yob',
|
|
208
|
+
)
|
|
209
|
+
expect(suggestion1?.handle).toBe('some-name80.test')
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('suggests random hyphens in the middle of handle', async () => {
|
|
213
|
+
const {
|
|
214
|
+
data: { result },
|
|
215
|
+
} = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
216
|
+
handle,
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
assertUnavailable(result)
|
|
220
|
+
const suggestion = result.suggestions.find((s) => s.method === 'hyphen')
|
|
221
|
+
expect(suggestion?.handle).not.toBe(handle)
|
|
222
|
+
expect(suggestion?.handle.replace('-', '')).toBe(handle)
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('suggests random digits at the end of handle', async () => {
|
|
226
|
+
const {
|
|
227
|
+
data: { result },
|
|
228
|
+
} = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
229
|
+
handle,
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
assertUnavailable(result)
|
|
233
|
+
const suggestion = result.suggestions.find(
|
|
234
|
+
(s) => s.method === 'random_digits',
|
|
235
|
+
)
|
|
236
|
+
expect(suggestion?.handle).not.toBe(handle)
|
|
237
|
+
expect(suggestion?.handle.replace(/\d+/, '')).toBe(handle)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('dedupes suggestions', async () => {
|
|
241
|
+
jest.spyOn(global.Math, 'random').mockReturnValue(0)
|
|
242
|
+
|
|
243
|
+
const {
|
|
244
|
+
data: { result },
|
|
245
|
+
} = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
246
|
+
handle,
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
assertUnavailable(result)
|
|
250
|
+
expect(result.suggestions).toStrictEqual([
|
|
251
|
+
{ handle: 'a-lice.test', method: 'hyphen' },
|
|
252
|
+
{ handle: 'al-ice.test', method: 'hyphen' },
|
|
253
|
+
{ handle: 'alice0.test', method: 'random_digits' },
|
|
254
|
+
])
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it(`avoids slurs`, async () => {
|
|
258
|
+
jest.spyOn(global.Math, 'random').mockReturnValue(0)
|
|
259
|
+
|
|
260
|
+
const bass = {
|
|
261
|
+
email: 'bass@mail.com',
|
|
262
|
+
handle: 'bass.test',
|
|
263
|
+
password: 'hunter2',
|
|
264
|
+
}
|
|
265
|
+
await sc.createAccount('bass', bass)
|
|
266
|
+
await network.processAll()
|
|
267
|
+
|
|
268
|
+
const {
|
|
269
|
+
data: { result },
|
|
270
|
+
} = await agent.app.bsky.unspecced.checkHandleAvailability({
|
|
271
|
+
handle: bass.handle,
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
assertUnavailable(result)
|
|
275
|
+
const suggestion = result.suggestions.find(
|
|
276
|
+
(s) => s.handle === 'b-ass.test',
|
|
277
|
+
)
|
|
278
|
+
expect(suggestion).toBeUndefined()
|
|
279
|
+
})
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
function assertAvailable(
|
|
285
|
+
r: OutputSchema['result'],
|
|
286
|
+
): asserts r is $Typed<ResultAvailable> {
|
|
287
|
+
assert(AppBskyUnspeccedCheckHandleAvailability.isResultAvailable(r))
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function assertUnavailable(
|
|
291
|
+
r: OutputSchema['result'],
|
|
292
|
+
): asserts r is $Typed<ResultUnavailable> {
|
|
293
|
+
assert(AppBskyUnspeccedCheckHandleAvailability.isResultUnavailable(r))
|
|
294
|
+
}
|