@atproto/bsky 0.0.173 → 0.0.175
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 +16 -0
- package/dist/api/app/bsky/unspecced/initAgeAssurance.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/initAgeAssurance.js +19 -5
- package/dist/api/app/bsky/unspecced/initAgeAssurance.js.map +1 -1
- package/dist/feature-gates.d.ts +1 -2
- package/dist/feature-gates.d.ts.map +1 -1
- package/dist/feature-gates.js +0 -1
- package/dist/feature-gates.js.map +1 -1
- package/dist/lexicon/index.d.ts +2 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +4 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +176 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +95 -0
- package/dist/lexicon/lexicons.js.map +1 -1
- 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/dist/lexicon/types/com/atproto/temp/checkHandleAvailability.d.ts +58 -0
- package/dist/lexicon/types/com/atproto/temp/checkHandleAvailability.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/temp/checkHandleAvailability.js +34 -0
- package/dist/lexicon/types/com/atproto/temp/checkHandleAvailability.js.map +1 -0
- package/package.json +7 -6
- package/src/api/app/bsky/unspecced/initAgeAssurance.ts +31 -7
- package/src/feature-gates.ts +0 -1
- package/src/lexicon/index.ts +13 -0
- package/src/lexicon/lexicons.ts +103 -0
- package/src/lexicon/types/app/bsky/unspecced/initAgeAssurance.ts +1 -1
- package/src/lexicon/types/com/atproto/temp/checkHandleAvailability.ts +99 -0
- package/tests/feed-generation.test.ts +6 -2
- package/tests/views/actor-search.test.ts +3 -1
- package/tests/views/age-assurance.test.ts +44 -3
- package/tests/views/author-feed.test.ts +3 -1
- package/tests/views/follows.test.ts +6 -2
- package/tests/views/likes.test.ts +3 -1
- package/tests/views/list-feed.test.ts +3 -1
- package/tests/views/mutes.test.ts +3 -1
- package/tests/views/notifications.test.ts +14 -7
- package/tests/views/reposts.test.ts +3 -1
- package/tests/views/timeline.test.ts +3 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -23,7 +23,7 @@ export interface HandlerSuccess {
|
|
|
23
23
|
export interface HandlerError {
|
|
24
24
|
status: number;
|
|
25
25
|
message?: string;
|
|
26
|
-
error?: 'InvalidEmail' | 'DidTooLong';
|
|
26
|
+
error?: 'InvalidEmail' | 'DidTooLong' | 'InvalidInitiation';
|
|
27
27
|
}
|
|
28
28
|
export type HandlerOutput = HandlerError | HandlerSuccess;
|
|
29
29
|
//# sourceMappingURL=initAgeAssurance.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initAgeAssurance.d.ts","sourceRoot":"","sources":["../../../../../../src/lexicon/types/app/bsky/unspecced/initAgeAssurance.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,KAAK,oBAAoB,MAAM,WAAW,CAAA;AAMtD,MAAM,MAAM,WAAW,GAAG,EAAE,CAAA;AAE5B,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,QAAQ,EAAE,MAAM,CAAA;IAChB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,MAAM,YAAY,GAAG,oBAAoB,CAAC,iBAAiB,CAAA;AAEjE,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,IAAI,EAAE,WAAW,CAAA;CAClB;AAED,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;IAChB,KAAK,CAAC,EAAE,cAAc,GAAG,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"initAgeAssurance.d.ts","sourceRoot":"","sources":["../../../../../../src/lexicon/types/app/bsky/unspecced/initAgeAssurance.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,KAAK,oBAAoB,MAAM,WAAW,CAAA;AAMtD,MAAM,MAAM,WAAW,GAAG,EAAE,CAAA;AAE5B,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,QAAQ,EAAE,MAAM,CAAA;IAChB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,MAAM,YAAY,GAAG,oBAAoB,CAAC,iBAAiB,CAAA;AAEjE,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,IAAI,EAAE,WAAW,CAAA;CAClB;AAED,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;IAChB,KAAK,CAAC,EAAE,cAAc,GAAG,YAAY,GAAG,mBAAmB,CAAA;CAC5D;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,CAAA"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED CODE - DO NOT MODIFY
|
|
3
|
+
*/
|
|
4
|
+
import { type ValidationResult } from '@atproto/lexicon';
|
|
5
|
+
import { type $Typed } from '../../../../util';
|
|
6
|
+
export type QueryParams = {
|
|
7
|
+
/** Tentative handle. Will be checked for availability or used to build handle suggestions. */
|
|
8
|
+
handle: string;
|
|
9
|
+
/** User-provided email. Might be used to build handle suggestions. */
|
|
10
|
+
email?: string;
|
|
11
|
+
/** User-provided birth date. Might be used to build handle suggestions. */
|
|
12
|
+
birthDate?: string;
|
|
13
|
+
};
|
|
14
|
+
export type InputSchema = undefined;
|
|
15
|
+
export interface OutputSchema {
|
|
16
|
+
/** Echo of the input handle. */
|
|
17
|
+
handle: string;
|
|
18
|
+
result: $Typed<ResultAvailable> | $Typed<ResultUnavailable> | {
|
|
19
|
+
$type: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export type HandlerInput = void;
|
|
23
|
+
export interface HandlerSuccess {
|
|
24
|
+
encoding: 'application/json';
|
|
25
|
+
body: OutputSchema;
|
|
26
|
+
headers?: {
|
|
27
|
+
[key: string]: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface HandlerError {
|
|
31
|
+
status: number;
|
|
32
|
+
message?: string;
|
|
33
|
+
error?: 'InvalidEmail';
|
|
34
|
+
}
|
|
35
|
+
export type HandlerOutput = HandlerError | HandlerSuccess;
|
|
36
|
+
/** Indicates the provided handle is available. */
|
|
37
|
+
export interface ResultAvailable {
|
|
38
|
+
$type?: 'com.atproto.temp.checkHandleAvailability#resultAvailable';
|
|
39
|
+
}
|
|
40
|
+
export declare function isResultAvailable<V>(v: V): v is import("../../../../util").$TypedObject<V, "com.atproto.temp.checkHandleAvailability", "resultAvailable">;
|
|
41
|
+
export declare function validateResultAvailable<V>(v: V): ValidationResult<ResultAvailable & V>;
|
|
42
|
+
/** Indicates the provided handle is unavailable and gives suggestions of available handles. */
|
|
43
|
+
export interface ResultUnavailable {
|
|
44
|
+
$type?: 'com.atproto.temp.checkHandleAvailability#resultUnavailable';
|
|
45
|
+
/** List of suggested handles based on the provided inputs. */
|
|
46
|
+
suggestions: Suggestion[];
|
|
47
|
+
}
|
|
48
|
+
export declare function isResultUnavailable<V>(v: V): v is import("../../../../util").$TypedObject<V, "com.atproto.temp.checkHandleAvailability", "resultUnavailable">;
|
|
49
|
+
export declare function validateResultUnavailable<V>(v: V): ValidationResult<ResultUnavailable & V>;
|
|
50
|
+
export interface Suggestion {
|
|
51
|
+
$type?: 'com.atproto.temp.checkHandleAvailability#suggestion';
|
|
52
|
+
handle: string;
|
|
53
|
+
/** Method used to build this suggestion. Should be considered opaque to clients. Can be used for metrics. */
|
|
54
|
+
method: string;
|
|
55
|
+
}
|
|
56
|
+
export declare function isSuggestion<V>(v: V): v is import("../../../../util").$TypedObject<V, "com.atproto.temp.checkHandleAvailability", "suggestion">;
|
|
57
|
+
export declare function validateSuggestion<V>(v: V): ValidationResult<Suggestion & V>;
|
|
58
|
+
//# sourceMappingURL=checkHandleAvailability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkHandleAvailability.d.ts","sourceRoot":"","sources":["../../../../../../src/lexicon/types/com/atproto/temp/checkHandleAvailability.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,KAAK,gBAAgB,EAAW,MAAM,kBAAkB,CAAA;AAGjE,OAAO,EACL,KAAK,MAAM,EAGZ,MAAM,kBAAkB,CAAA;AAMzB,MAAM,MAAM,WAAW,GAAG;IACxB,8FAA8F;IAC9F,MAAM,EAAE,MAAM,CAAA;IACd,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AACD,MAAM,MAAM,WAAW,GAAG,SAAS,CAAA;AAEnC,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EACF,MAAM,CAAC,eAAe,CAAC,GACvB,MAAM,CAAC,iBAAiB,CAAC,GACzB;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CACtB;AAED,MAAM,MAAM,YAAY,GAAG,IAAI,CAAA;AAE/B,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;IAChB,KAAK,CAAC,EAAE,cAAc,CAAA;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,CAAA;AAEzD,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,0DAA0D,CAAA;CACnE;AAID,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,kHAExC;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,yCAE9C;AAED,+FAA+F;AAC/F,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,4DAA4D,CAAA;IACpE,8DAA8D;IAC9D,WAAW,EAAE,UAAU,EAAE,CAAA;CAC1B;AAID,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,oHAE1C;AAED,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,2CAEhD;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,qDAAqD,CAAA;IAC7D,MAAM,EAAE,MAAM,CAAA;IACd,6GAA6G;IAC7G,MAAM,EAAE,MAAM,CAAA;CACf;AAID,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,6GAEnC;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,oCAEzC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isResultAvailable = isResultAvailable;
|
|
4
|
+
exports.validateResultAvailable = validateResultAvailable;
|
|
5
|
+
exports.isResultUnavailable = isResultUnavailable;
|
|
6
|
+
exports.validateResultUnavailable = validateResultUnavailable;
|
|
7
|
+
exports.isSuggestion = isSuggestion;
|
|
8
|
+
exports.validateSuggestion = validateSuggestion;
|
|
9
|
+
const lexicons_1 = require("../../../../lexicons");
|
|
10
|
+
const util_1 = require("../../../../util");
|
|
11
|
+
const is$typed = util_1.is$typed, validate = lexicons_1.validate;
|
|
12
|
+
const id = 'com.atproto.temp.checkHandleAvailability';
|
|
13
|
+
const hashResultAvailable = 'resultAvailable';
|
|
14
|
+
function isResultAvailable(v) {
|
|
15
|
+
return is$typed(v, id, hashResultAvailable);
|
|
16
|
+
}
|
|
17
|
+
function validateResultAvailable(v) {
|
|
18
|
+
return validate(v, id, hashResultAvailable);
|
|
19
|
+
}
|
|
20
|
+
const hashResultUnavailable = 'resultUnavailable';
|
|
21
|
+
function isResultUnavailable(v) {
|
|
22
|
+
return is$typed(v, id, hashResultUnavailable);
|
|
23
|
+
}
|
|
24
|
+
function validateResultUnavailable(v) {
|
|
25
|
+
return validate(v, id, hashResultUnavailable);
|
|
26
|
+
}
|
|
27
|
+
const hashSuggestion = 'suggestion';
|
|
28
|
+
function isSuggestion(v) {
|
|
29
|
+
return is$typed(v, id, hashSuggestion);
|
|
30
|
+
}
|
|
31
|
+
function validateSuggestion(v) {
|
|
32
|
+
return validate(v, id, hashSuggestion);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=checkHandleAvailability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkHandleAvailability.js","sourceRoot":"","sources":["../../../../../../src/lexicon/types/com/atproto/temp/checkHandleAvailability.ts"],"names":[],"mappings":";;AA0DA,8CAEC;AAED,0DAEC;AAWD,kDAEC;AAED,8DAEC;AAWD,oCAEC;AAED,gDAEC;AA7FD,mDAA4D;AAC5D,2CAIyB;AAEzB,MAAM,QAAQ,GAAG,eAAS,EACxB,QAAQ,GAAG,mBAAS,CAAA;AACtB,MAAM,EAAE,GAAG,0CAA0C,CAAA;AA0CrD,MAAM,mBAAmB,GAAG,iBAAiB,CAAA;AAE7C,SAAgB,iBAAiB,CAAI,CAAI;IACvC,OAAO,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,mBAAmB,CAAC,CAAA;AAC7C,CAAC;AAED,SAAgB,uBAAuB,CAAI,CAAI;IAC7C,OAAO,QAAQ,CAAsB,CAAC,EAAE,EAAE,EAAE,mBAAmB,CAAC,CAAA;AAClE,CAAC;AASD,MAAM,qBAAqB,GAAG,mBAAmB,CAAA;AAEjD,SAAgB,mBAAmB,CAAI,CAAI;IACzC,OAAO,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,qBAAqB,CAAC,CAAA;AAC/C,CAAC;AAED,SAAgB,yBAAyB,CAAI,CAAI;IAC/C,OAAO,QAAQ,CAAwB,CAAC,EAAE,EAAE,EAAE,qBAAqB,CAAC,CAAA;AACtE,CAAC;AASD,MAAM,cAAc,GAAG,YAAY,CAAA;AAEnC,SAAgB,YAAY,CAAI,CAAI;IAClC,OAAO,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,cAAc,CAAC,CAAA;AACxC,CAAC;AAED,SAAgB,kBAAkB,CAAI,CAAI;IACxC,OAAO,QAAQ,CAAiB,CAAC,EAAE,EAAE,EAAE,cAAc,CAAC,CAAA;AACxD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/bsky",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.175",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Reference implementation of app.bsky App View (Bluesky API)",
|
|
6
6
|
"keywords": [
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"jose": "^5.0.1",
|
|
38
38
|
"key-encoder": "^2.0.3",
|
|
39
39
|
"kysely": "^0.22.0",
|
|
40
|
+
"leo-profanity": "^1.8.0",
|
|
40
41
|
"multiformats": "^9.9.0",
|
|
41
42
|
"murmurhash": "^2.0.1",
|
|
42
43
|
"p-queue": "^6.6.2",
|
|
@@ -52,10 +53,10 @@
|
|
|
52
53
|
"zod": "3.23.8",
|
|
53
54
|
"@atproto-labs/fetch-node": "0.1.9",
|
|
54
55
|
"@atproto-labs/xrpc-utils": "0.0.17",
|
|
56
|
+
"@atproto/api": "^0.16.0",
|
|
55
57
|
"@atproto/common": "^0.4.11",
|
|
56
|
-
"@atproto/api": "^0.15.26",
|
|
57
|
-
"@atproto/crypto": "^0.4.4",
|
|
58
58
|
"@atproto/did": "^0.1.5",
|
|
59
|
+
"@atproto/crypto": "^0.4.4",
|
|
59
60
|
"@atproto/identity": "^0.4.8",
|
|
60
61
|
"@atproto/lexicon": "^0.4.12",
|
|
61
62
|
"@atproto/repo": "^0.8.5",
|
|
@@ -76,9 +77,9 @@
|
|
|
76
77
|
"jest": "^28.1.2",
|
|
77
78
|
"ts-node": "^10.8.2",
|
|
78
79
|
"typescript": "^5.6.3",
|
|
79
|
-
"@atproto/api": "^0.
|
|
80
|
-
"@atproto/lex-cli": "^0.9.
|
|
81
|
-
"@atproto/pds": "^0.4.
|
|
80
|
+
"@atproto/api": "^0.16.0",
|
|
81
|
+
"@atproto/lex-cli": "^0.9.1",
|
|
82
|
+
"@atproto/pds": "^0.4.163",
|
|
82
83
|
"@atproto/xrpc": "^0.7.1"
|
|
83
84
|
},
|
|
84
85
|
"scripts": {
|
|
@@ -2,16 +2,15 @@ import crypto from 'node:crypto'
|
|
|
2
2
|
import { isEmailValid } from '@hapi/address'
|
|
3
3
|
import { isDisposableEmail } from 'disposable-email-domains-js'
|
|
4
4
|
import {
|
|
5
|
-
ForbiddenError,
|
|
6
5
|
InvalidRequestError,
|
|
7
6
|
MethodNotImplementedError,
|
|
8
7
|
} from '@atproto/xrpc-server'
|
|
9
8
|
import { AppContext } from '../../../../context'
|
|
10
|
-
import { GateID } from '../../../../feature-gates'
|
|
11
9
|
import { KwsExternalPayloadError } from '../../../../kws'
|
|
12
10
|
import { Server } from '../../../../lexicon'
|
|
13
11
|
import { InputSchema } from '../../../../lexicon/types/app/bsky/unspecced/initAgeAssurance'
|
|
14
12
|
import { httpLogger as log } from '../../../../logger'
|
|
13
|
+
import { ActorInfo } from '../../../../proto/bsky_pb'
|
|
15
14
|
import { KwsExternalPayload } from '../../../kws/types'
|
|
16
15
|
import { createStashEvent, getClientUa } from '../../../kws/util'
|
|
17
16
|
|
|
@@ -26,11 +25,19 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
const actorDid = auth.credentials.iss
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
|
|
28
|
+
|
|
29
|
+
const actorInfo = await getAgeVerificationState(ctx, actorDid)
|
|
30
|
+
|
|
31
|
+
if (actorInfo?.ageAssuranceStatus) {
|
|
32
|
+
if (
|
|
33
|
+
actorInfo.ageAssuranceStatus.status !== 'unknown' &&
|
|
34
|
+
actorInfo.ageAssuranceStatus.status !== 'pending'
|
|
35
|
+
) {
|
|
36
|
+
throw new InvalidRequestError(
|
|
37
|
+
`Cannot initiate age assurance flow from current state: ${actorInfo.ageAssuranceStatus.status}`,
|
|
38
|
+
'InvalidInitiation',
|
|
39
|
+
)
|
|
40
|
+
}
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
const { countryCode, email, language } = validateInput(input.body)
|
|
@@ -122,3 +129,20 @@ const validateInput = (input: InputSchema): InputSchema => {
|
|
|
122
129
|
language: kwsAvSupportedLanguages.includes(language) ? language : 'en',
|
|
123
130
|
}
|
|
124
131
|
}
|
|
132
|
+
|
|
133
|
+
const getAgeVerificationState = async (
|
|
134
|
+
ctx: AppContext,
|
|
135
|
+
actorDid: string,
|
|
136
|
+
): Promise<ActorInfo | undefined> => {
|
|
137
|
+
try {
|
|
138
|
+
const res = await ctx.dataplane.getActors({
|
|
139
|
+
dids: [actorDid],
|
|
140
|
+
returnAgeAssuranceForDids: [actorDid],
|
|
141
|
+
skipCacheForDids: [actorDid],
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
return res.actors[0]
|
|
145
|
+
} catch (err) {
|
|
146
|
+
return undefined
|
|
147
|
+
}
|
|
148
|
+
}
|
package/src/feature-gates.ts
CHANGED
package/src/lexicon/index.ts
CHANGED
|
@@ -89,6 +89,7 @@ import * as ComAtprotoSyncNotifyOfUpdate from './types/com/atproto/sync/notifyOf
|
|
|
89
89
|
import * as ComAtprotoSyncRequestCrawl from './types/com/atproto/sync/requestCrawl.js'
|
|
90
90
|
import * as ComAtprotoSyncSubscribeRepos from './types/com/atproto/sync/subscribeRepos.js'
|
|
91
91
|
import * as ComAtprotoTempAddReservedHandle from './types/com/atproto/temp/addReservedHandle.js'
|
|
92
|
+
import * as ComAtprotoTempCheckHandleAvailability from './types/com/atproto/temp/checkHandleAvailability.js'
|
|
92
93
|
import * as ComAtprotoTempCheckSignupQueue from './types/com/atproto/temp/checkSignupQueue.js'
|
|
93
94
|
import * as ComAtprotoTempFetchLabels from './types/com/atproto/temp/fetchLabels.js'
|
|
94
95
|
import * as ComAtprotoTempRequestPhoneVerification from './types/com/atproto/temp/requestPhoneVerification.js'
|
|
@@ -1300,6 +1301,18 @@ export class ComAtprotoTempNS {
|
|
|
1300
1301
|
return this._server.xrpc.method(nsid, cfg)
|
|
1301
1302
|
}
|
|
1302
1303
|
|
|
1304
|
+
checkHandleAvailability<A extends Auth = void>(
|
|
1305
|
+
cfg: MethodConfigOrHandler<
|
|
1306
|
+
A,
|
|
1307
|
+
ComAtprotoTempCheckHandleAvailability.QueryParams,
|
|
1308
|
+
ComAtprotoTempCheckHandleAvailability.HandlerInput,
|
|
1309
|
+
ComAtprotoTempCheckHandleAvailability.HandlerOutput
|
|
1310
|
+
>,
|
|
1311
|
+
) {
|
|
1312
|
+
const nsid = 'com.atproto.temp.checkHandleAvailability' // @ts-ignore
|
|
1313
|
+
return this._server.xrpc.method(nsid, cfg)
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1303
1316
|
checkSignupQueue<A extends Auth = void>(
|
|
1304
1317
|
cfg: MethodConfigOrHandler<
|
|
1305
1318
|
A,
|
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -4449,6 +4449,104 @@ export const schemaDict = {
|
|
|
4449
4449
|
},
|
|
4450
4450
|
},
|
|
4451
4451
|
},
|
|
4452
|
+
ComAtprotoTempCheckHandleAvailability: {
|
|
4453
|
+
lexicon: 1,
|
|
4454
|
+
id: 'com.atproto.temp.checkHandleAvailability',
|
|
4455
|
+
defs: {
|
|
4456
|
+
main: {
|
|
4457
|
+
type: 'query',
|
|
4458
|
+
description:
|
|
4459
|
+
'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.',
|
|
4460
|
+
parameters: {
|
|
4461
|
+
type: 'params',
|
|
4462
|
+
required: ['handle'],
|
|
4463
|
+
properties: {
|
|
4464
|
+
handle: {
|
|
4465
|
+
type: 'string',
|
|
4466
|
+
format: 'handle',
|
|
4467
|
+
description:
|
|
4468
|
+
'Tentative handle. Will be checked for availability or used to build handle suggestions.',
|
|
4469
|
+
},
|
|
4470
|
+
email: {
|
|
4471
|
+
type: 'string',
|
|
4472
|
+
description:
|
|
4473
|
+
'User-provided email. Might be used to build handle suggestions.',
|
|
4474
|
+
},
|
|
4475
|
+
birthDate: {
|
|
4476
|
+
type: 'string',
|
|
4477
|
+
format: 'datetime',
|
|
4478
|
+
description:
|
|
4479
|
+
'User-provided birth date. Might be used to build handle suggestions.',
|
|
4480
|
+
},
|
|
4481
|
+
},
|
|
4482
|
+
},
|
|
4483
|
+
output: {
|
|
4484
|
+
encoding: 'application/json',
|
|
4485
|
+
schema: {
|
|
4486
|
+
type: 'object',
|
|
4487
|
+
required: ['handle', 'result'],
|
|
4488
|
+
properties: {
|
|
4489
|
+
handle: {
|
|
4490
|
+
type: 'string',
|
|
4491
|
+
format: 'handle',
|
|
4492
|
+
description: 'Echo of the input handle.',
|
|
4493
|
+
},
|
|
4494
|
+
result: {
|
|
4495
|
+
type: 'union',
|
|
4496
|
+
refs: [
|
|
4497
|
+
'lex:com.atproto.temp.checkHandleAvailability#resultAvailable',
|
|
4498
|
+
'lex:com.atproto.temp.checkHandleAvailability#resultUnavailable',
|
|
4499
|
+
],
|
|
4500
|
+
},
|
|
4501
|
+
},
|
|
4502
|
+
},
|
|
4503
|
+
},
|
|
4504
|
+
errors: [
|
|
4505
|
+
{
|
|
4506
|
+
name: 'InvalidEmail',
|
|
4507
|
+
description: 'An invalid email was provided.',
|
|
4508
|
+
},
|
|
4509
|
+
],
|
|
4510
|
+
},
|
|
4511
|
+
resultAvailable: {
|
|
4512
|
+
type: 'object',
|
|
4513
|
+
description: 'Indicates the provided handle is available.',
|
|
4514
|
+
properties: {},
|
|
4515
|
+
},
|
|
4516
|
+
resultUnavailable: {
|
|
4517
|
+
type: 'object',
|
|
4518
|
+
description:
|
|
4519
|
+
'Indicates the provided handle is unavailable and gives suggestions of available handles.',
|
|
4520
|
+
required: ['suggestions'],
|
|
4521
|
+
properties: {
|
|
4522
|
+
suggestions: {
|
|
4523
|
+
type: 'array',
|
|
4524
|
+
description:
|
|
4525
|
+
'List of suggested handles based on the provided inputs.',
|
|
4526
|
+
items: {
|
|
4527
|
+
type: 'ref',
|
|
4528
|
+
ref: 'lex:com.atproto.temp.checkHandleAvailability#suggestion',
|
|
4529
|
+
},
|
|
4530
|
+
},
|
|
4531
|
+
},
|
|
4532
|
+
},
|
|
4533
|
+
suggestion: {
|
|
4534
|
+
type: 'object',
|
|
4535
|
+
required: ['handle', 'method'],
|
|
4536
|
+
properties: {
|
|
4537
|
+
handle: {
|
|
4538
|
+
type: 'string',
|
|
4539
|
+
format: 'handle',
|
|
4540
|
+
},
|
|
4541
|
+
method: {
|
|
4542
|
+
type: 'string',
|
|
4543
|
+
description:
|
|
4544
|
+
'Method used to build this suggestion. Should be considered opaque to clients. Can be used for metrics.',
|
|
4545
|
+
},
|
|
4546
|
+
},
|
|
4547
|
+
},
|
|
4548
|
+
},
|
|
4549
|
+
},
|
|
4452
4550
|
ComAtprotoTempCheckSignupQueue: {
|
|
4453
4551
|
lexicon: 1,
|
|
4454
4552
|
id: 'com.atproto.temp.checkSignupQueue',
|
|
@@ -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
|
},
|
|
@@ -13436,6 +13537,8 @@ export const ids = {
|
|
|
13436
13537
|
ComAtprotoSyncRequestCrawl: 'com.atproto.sync.requestCrawl',
|
|
13437
13538
|
ComAtprotoSyncSubscribeRepos: 'com.atproto.sync.subscribeRepos',
|
|
13438
13539
|
ComAtprotoTempAddReservedHandle: 'com.atproto.temp.addReservedHandle',
|
|
13540
|
+
ComAtprotoTempCheckHandleAvailability:
|
|
13541
|
+
'com.atproto.temp.checkHandleAvailability',
|
|
13439
13542
|
ComAtprotoTempCheckSignupQueue: 'com.atproto.temp.checkSignupQueue',
|
|
13440
13543
|
ComAtprotoTempFetchLabels: 'com.atproto.temp.fetchLabels',
|
|
13441
13544
|
ComAtprotoTempRequestPhoneVerification:
|
|
@@ -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
|
|
@@ -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 = 'com.atproto.temp.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?: 'com.atproto.temp.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?: 'com.atproto.temp.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?: 'com.atproto.temp.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
|
+
}
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
FeedViewPost,
|
|
16
16
|
SkeletonFeedPost,
|
|
17
17
|
} from '../src/lexicon/types/app/bsky/feed/defs'
|
|
18
|
+
import { OutputSchema as GetActorFeedsOutputSchema } from '../src/lexicon/types/app/bsky/feed/getActorFeeds'
|
|
19
|
+
import { OutputSchema as GetFeedOutputSchema } from '../src/lexicon/types/app/bsky/feed/getFeed'
|
|
18
20
|
import * as AppBskyFeedGetFeedSkeleton from '../src/lexicon/types/app/bsky/feed/getFeedSkeleton'
|
|
19
21
|
import { forSnapshot, paginateAll } from './_util'
|
|
20
22
|
|
|
@@ -226,7 +228,8 @@ describe('feed generation', () => {
|
|
|
226
228
|
await sc.like(sc.dids.carol, feedUriAllRef)
|
|
227
229
|
await network.processAll()
|
|
228
230
|
|
|
229
|
-
const results = (results
|
|
231
|
+
const results = (results: GetActorFeedsOutputSchema[]) =>
|
|
232
|
+
results.flatMap((res) => res.feeds)
|
|
230
233
|
const paginator = async (cursor?: string) => {
|
|
231
234
|
const res = await agent.api.app.bsky.feed.getActorFeeds(
|
|
232
235
|
{ actor: alice, cursor, limit: 2 },
|
|
@@ -632,7 +635,8 @@ describe('feed generation', () => {
|
|
|
632
635
|
})
|
|
633
636
|
|
|
634
637
|
it('paginates, handling replies and reposts.', async () => {
|
|
635
|
-
const results = (results
|
|
638
|
+
const results = (results: GetFeedOutputSchema[]) =>
|
|
639
|
+
results.flatMap((res) => res.feed)
|
|
636
640
|
const paginator = async (cursor?: string) => {
|
|
637
641
|
const res = await agent.api.app.bsky.feed.getFeed(
|
|
638
642
|
{ feed: feedUriAll, cursor, limit: 2 },
|
|
@@ -2,6 +2,7 @@ import { AtpAgent } from '@atproto/api'
|
|
|
2
2
|
import { wait } from '@atproto/common'
|
|
3
3
|
import { SeedClient, TestNetwork, usersBulkSeed } from '@atproto/dev-env'
|
|
4
4
|
import { ids } from '../../src/lexicon/lexicons'
|
|
5
|
+
import { OutputSchema as SearchActorsOutputSchema } from '../../src/lexicon/types/app/bsky/actor/searchActors'
|
|
5
6
|
import { forSnapshot, paginateAll, stripViewer } from '../_util'
|
|
6
7
|
|
|
7
8
|
// @NOTE skipped to help with CI failures
|
|
@@ -188,7 +189,8 @@ describe.skip('pds actor search views', () => {
|
|
|
188
189
|
})
|
|
189
190
|
|
|
190
191
|
it('paginates', async () => {
|
|
191
|
-
const results = (results
|
|
192
|
+
const results = (results: SearchActorsOutputSchema[]) =>
|
|
193
|
+
results.flatMap((res) => res.actors)
|
|
192
194
|
const paginator = async (cursor?: string) => {
|
|
193
195
|
const res = await agent.api.app.bsky.actor.searchActors(
|
|
194
196
|
{ term: 'p', cursor, limit: 3 },
|
|
@@ -3,7 +3,6 @@ import { once } from 'node:events'
|
|
|
3
3
|
import { Server, createServer } from 'node:http'
|
|
4
4
|
import { AddressInfo } from 'node:net'
|
|
5
5
|
import express, { Application } from 'express'
|
|
6
|
-
import Statsig from 'statsig-node'
|
|
7
6
|
import { AtpAgent } from '@atproto/api'
|
|
8
7
|
import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
|
|
9
8
|
import {
|
|
@@ -15,7 +14,6 @@ import {
|
|
|
15
14
|
parseExternalPayload,
|
|
16
15
|
serializeExternalPayload,
|
|
17
16
|
} from '../../src/api/kws/util'
|
|
18
|
-
import { GateID } from '../../src/feature-gates'
|
|
19
17
|
import { ids } from '../../src/lexicon/lexicons'
|
|
20
18
|
|
|
21
19
|
type Database = TestNetwork['bsky']['db']
|
|
@@ -63,7 +61,6 @@ describe('age assurance views', () => {
|
|
|
63
61
|
},
|
|
64
62
|
},
|
|
65
63
|
})
|
|
66
|
-
Statsig.overrideGate(GateID.AgeAssurance, true)
|
|
67
64
|
db = network.bsky.db
|
|
68
65
|
agent = network.bsky.getClient()
|
|
69
66
|
sc = network.getSeedClient()
|
|
@@ -189,6 +186,50 @@ describe('age assurance views', () => {
|
|
|
189
186
|
)
|
|
190
187
|
})
|
|
191
188
|
|
|
189
|
+
it('ensures user cannot re-init flow from terminal state', async () => {
|
|
190
|
+
const actor = sc.dids.bob
|
|
191
|
+
const state0 = await getAgeAssurance(actor)
|
|
192
|
+
expect(state0).toStrictEqual({
|
|
193
|
+
status: 'unknown',
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
const init1 = await initAgeAssurance(actor)
|
|
197
|
+
expect(init1).toStrictEqual({
|
|
198
|
+
status: 'pending',
|
|
199
|
+
lastInitiatedAt: expect.any(String),
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const init2 = await initAgeAssurance(actor)
|
|
203
|
+
expect(init2).toStrictEqual({
|
|
204
|
+
status: 'pending',
|
|
205
|
+
lastInitiatedAt: expect.any(String),
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Can re-init flow if the state is pending.
|
|
210
|
+
*/
|
|
211
|
+
expect(sendEmailMock).toHaveBeenCalledTimes(2)
|
|
212
|
+
|
|
213
|
+
const externalPayload: KwsExternalPayload = {
|
|
214
|
+
actorDid: actor,
|
|
215
|
+
attemptId,
|
|
216
|
+
}
|
|
217
|
+
const status = { verified: true }
|
|
218
|
+
await kwsServer.callVerificationResponse(network.bsky.url, {
|
|
219
|
+
externalPayload,
|
|
220
|
+
status,
|
|
221
|
+
})
|
|
222
|
+
const finalizedState = await getAgeAssurance(actor)
|
|
223
|
+
expect(finalizedState).toStrictEqual({
|
|
224
|
+
status: 'assured',
|
|
225
|
+
lastInitiatedAt: expect.any(String),
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
await expect(initAgeAssurance(actor)).rejects.toThrowError(
|
|
229
|
+
`Cannot initiate age assurance flow from current state: assured`,
|
|
230
|
+
)
|
|
231
|
+
})
|
|
232
|
+
|
|
192
233
|
describe('verification response flow', () => {
|
|
193
234
|
it('performs the AA flow', async () => {
|
|
194
235
|
const state0 = await getAgeAssurance(actorDid)
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
isPostView,
|
|
14
14
|
isReasonPin,
|
|
15
15
|
} from '../../src/lexicon/types/app/bsky/feed/defs'
|
|
16
|
+
import { OutputSchema as GetAuthorFeedOutputSchema } from '../../src/lexicon/types/app/bsky/feed/getAuthorFeed'
|
|
16
17
|
import {
|
|
17
18
|
ReplyRef,
|
|
18
19
|
isRecord,
|
|
@@ -138,7 +139,8 @@ describe('pds author feed views', () => {
|
|
|
138
139
|
})
|
|
139
140
|
|
|
140
141
|
it('paginates', async () => {
|
|
141
|
-
const results = (results
|
|
142
|
+
const results = (results: GetAuthorFeedOutputSchema[]) =>
|
|
143
|
+
results.flatMap((res) => res.feed)
|
|
142
144
|
const paginator = async (cursor?: string) => {
|
|
143
145
|
const res = await agent.api.app.bsky.feed.getAuthorFeed(
|
|
144
146
|
{
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { AtpAgent } from '@atproto/api'
|
|
2
2
|
import { SeedClient, TestNetwork, followsSeed } from '@atproto/dev-env'
|
|
3
3
|
import { ids } from '../../src/lexicon/lexicons'
|
|
4
|
+
import { OutputSchema as GetFollowersOutputSchema } from '../../src/lexicon/types/app/bsky/graph/getFollowers'
|
|
5
|
+
import { OutputSchema as GetFollowsOutputSchema } from '../../src/lexicon/types/app/bsky/graph/getFollows'
|
|
4
6
|
import { forSnapshot, paginateAll, stripViewer } from '../_util'
|
|
5
7
|
|
|
6
8
|
describe('pds follow views', () => {
|
|
@@ -114,7 +116,8 @@ describe('pds follow views', () => {
|
|
|
114
116
|
})
|
|
115
117
|
|
|
116
118
|
it('paginates followers', async () => {
|
|
117
|
-
const results = (results
|
|
119
|
+
const results = (results: GetFollowersOutputSchema[]) =>
|
|
120
|
+
results.flatMap((res) => res.followers)
|
|
118
121
|
const paginator = async (cursor?: string) => {
|
|
119
122
|
const res = await agent.api.app.bsky.graph.getFollowers(
|
|
120
123
|
{
|
|
@@ -277,7 +280,8 @@ describe('pds follow views', () => {
|
|
|
277
280
|
})
|
|
278
281
|
|
|
279
282
|
it('paginates follows', async () => {
|
|
280
|
-
const results = (results
|
|
283
|
+
const results = (results: GetFollowsOutputSchema[]) =>
|
|
284
|
+
results.flatMap((res) => res.follows)
|
|
281
285
|
const paginator = async (cursor?: string) => {
|
|
282
286
|
const res = await agent.api.app.bsky.graph.getFollows(
|
|
283
287
|
{
|