@atproto/bsky 0.0.249 → 0.0.250
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/age-assurance/const.d.ts.map +1 -1
- package/dist/api/age-assurance/const.js +19 -0
- package/dist/api/age-assurance/const.js.map +1 -1
- package/dist/api/age-assurance/kws/age-verified.d.ts +20 -0
- package/dist/api/age-assurance/kws/age-verified.d.ts.map +1 -1
- package/dist/api/age-assurance/kws/age-verified.js +6 -0
- package/dist/api/age-assurance/kws/age-verified.js.map +1 -1
- package/dist/api/app/bsky/graph/muteActor.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/muteActor.js +3 -0
- package/dist/api/app/bsky/graph/muteActor.js.map +1 -1
- package/dist/api/kws/types.d.ts +33 -0
- package/dist/api/kws/types.d.ts.map +1 -1
- package/dist/api/kws/types.js +3 -0
- package/dist/api/kws/types.js.map +1 -1
- package/dist/data-plane/index.d.ts +0 -1
- package/dist/data-plane/index.d.ts.map +1 -1
- package/dist/data-plane/index.js +0 -1
- package/dist/data-plane/index.js.map +1 -1
- package/dist/data-plane/server/bsync-subscription.d.ts +47 -0
- package/dist/data-plane/server/bsync-subscription.d.ts.map +1 -0
- package/dist/data-plane/server/bsync-subscription.js +461 -0
- package/dist/data-plane/server/bsync-subscription.js.map +1 -0
- package/dist/data-plane/server/index.d.ts +1 -0
- package/dist/data-plane/server/index.d.ts.map +1 -1
- package/dist/data-plane/server/index.js +1 -0
- package/dist/data-plane/server/index.js.map +1 -1
- package/dist/lexicons/app/bsky/ageassurance/defs.defs.d.ts +4 -0
- package/dist/lexicons/app/bsky/ageassurance/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/ageassurance/defs.defs.js +3 -0
- package/dist/lexicons/app/bsky/ageassurance/defs.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/convo/defs.defs.js +1 -1
- package/dist/lexicons/chat/bsky/convo/defs.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/group/editGroup.defs.d.ts +4 -4
- package/dist/lexicons/chat/bsky/group/editGroup.defs.js +2 -2
- package/dist/lexicons/chat/bsky/group/editGroup.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/moderation/defs.defs.js +1 -1
- package/dist/lexicons/chat/bsky/moderation/defs.defs.js.map +1 -1
- package/package.json +9 -9
- package/src/api/age-assurance/const.ts +19 -0
- package/src/api/age-assurance/kws/age-verified.test.ts +98 -0
- package/src/api/age-assurance/kws/age-verified.ts +6 -0
- package/src/api/app/bsky/graph/muteActor.ts +3 -0
- package/src/api/kws/types.ts +11 -0
- package/src/api/kws/util.test.ts +50 -0
- package/src/data-plane/index.ts +0 -1
- package/src/data-plane/{bsync/index.ts → server/bsync-subscription.ts} +245 -121
- package/src/data-plane/server/index.ts +1 -0
- package/tests/data-plane/thread-mutes.test.ts +2 -0
- package/tests/stash.test.ts +6 -1
- package/tests/views/age-assurance-v2.test.ts +3 -0
- package/tests/views/age-assurance.test.ts +8 -0
- package/tests/views/bookmarks.test.ts +19 -0
- package/tests/views/drafts.test.ts +25 -2
- package/tests/views/mute-lists.test.ts +2 -0
- package/tests/views/mutes.test.ts +1 -0
- package/tests/views/notifications.test.ts +16 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/data-plane/bsync/index.d.ts +0 -11
- package/dist/data-plane/bsync/index.d.ts.map +0 -1
- package/dist/data-plane/bsync/index.js +0 -376
- package/dist/data-plane/bsync/index.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defs.defs.js","sourceRoot":"","sources":["../../../../../src/lexicons/chat/bsky/moderation/defs.defs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAChC,OAAO,KAAK,SAAS,MAAM,uBAAuB,CAAA;AAClD,OAAO,KAAK,SAAS,MAAM,uBAAuB,CAAA;AAElD,MAAM,KAAK,GAAG,2BAA2B,CAAA;AAEzC,OAAO,EAAE,KAAK,EAAE,CAAA;AAgBhB,6WAA6W;AAC7W,MAAM,SAAS,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CAC3C,KAAK,EACL,WAAW;AACX,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;IACrB,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,EAAE;IAC5B,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,EAAE;IAC7B,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ;IAC5B,aAAa,CAAC,CAAC,CAAC,UAAU,CACxB;QACE,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAc,CAAC,GAAG,EAAE,CAAC,WAAW,CAAQ,CAAC;QACjE,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAa,CAAC,GAAG,EAAE,CAAC,UAAU,CAAQ,CAAC;KAChE,EACD,KAAK,CACN,CACF;CACF,CAAC,CACH,CAAA;AAED,OAAO,EAAE,SAAS,EAAE,CAAA;AAOpB,uEAAuE;AACvE,MAAM,WAAW,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CAC7C,KAAK,EACL,aAAa;AACb,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAC3B,CAAA;AAED,OAAO,EAAE,WAAW,EAAE,CAAA;AAoCtB,kQAAkQ;AAClQ,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CAC5C,KAAK,EACL,YAAY;AACZ,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;IACrB,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACzD,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ;IAChC,aAAa,CAAC,CAAC,CAAC,GAAG,CACjB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,YAAY,CAAQ,CACtC,CACF;IACD,gBAAgB,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE;IAC3C,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,CAC7B,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,eAAe,CAAQ,CACzC;IACD,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE;IACtC,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE;IACtC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"defs.defs.js","sourceRoot":"","sources":["../../../../../src/lexicons/chat/bsky/moderation/defs.defs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAChC,OAAO,KAAK,SAAS,MAAM,uBAAuB,CAAA;AAClD,OAAO,KAAK,SAAS,MAAM,uBAAuB,CAAA;AAElD,MAAM,KAAK,GAAG,2BAA2B,CAAA;AAEzC,OAAO,EAAE,KAAK,EAAE,CAAA;AAgBhB,6WAA6W;AAC7W,MAAM,SAAS,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CAC3C,KAAK,EACL,WAAW;AACX,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;IACrB,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,EAAE;IAC5B,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,EAAE;IAC7B,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ;IAC5B,aAAa,CAAC,CAAC,CAAC,UAAU,CACxB;QACE,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAc,CAAC,GAAG,EAAE,CAAC,WAAW,CAAQ,CAAC;QACjE,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAa,CAAC,GAAG,EAAE,CAAC,UAAU,CAAQ,CAAC;KAChE,EACD,KAAK,CACN,CACF;CACF,CAAC,CACH,CAAA;AAED,OAAO,EAAE,SAAS,EAAE,CAAA;AAOpB,uEAAuE;AACvE,MAAM,WAAW,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CAC7C,KAAK,EACL,aAAa;AACb,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAC3B,CAAA;AAED,OAAO,EAAE,WAAW,EAAE,CAAA;AAoCtB,kQAAkQ;AAClQ,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CAC5C,KAAK,EACL,YAAY;AACZ,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;IACrB,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACzD,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ;IAChC,aAAa,CAAC,CAAC,CAAC,GAAG,CACjB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,YAAY,CAAQ,CACtC,CACF;IACD,gBAAgB,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE;IAC3C,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,CAC7B,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,eAAe,CAAQ,CACzC;IACD,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE;IACtC,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE;IACtC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;CACnE,CAAC,CACH,CAAA;AAED,OAAO,EAAE,UAAU,EAAE,CAAA","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nimport { l } from '@atproto/lex'\nimport * as GroupDefs from '../group/defs.defs.js'\nimport * as ConvoDefs from '../convo/defs.defs.js'\n\nconst $nsid = 'chat.bsky.moderation.defs'\n\nexport { $nsid }\n\n/** A view of a conversation for moderation purposes. Unlike chat.bsky.convo.defs#convoView, it does not include viewer-specific data (such as muted, unreadCount, status, lastMessage, lastReaction), since the requester is a moderator and not a member of the conversation. The member list is not included; use chat.bsky.moderation.getConvoMembers to list members. */\ntype ConvoView = {\n $type?: 'chat.bsky.moderation.defs#convoView'\n id: string\n rev: string\n\n /**\n * Union field that has data specific to different kinds of convos.\n */\n kind?: l.$Typed<DirectConvo> | l.$Typed<GroupConvo> | l.Unknown$TypedObject\n}\n\nexport type { ConvoView }\n\n/** A view of a conversation for moderation purposes. Unlike chat.bsky.convo.defs#convoView, it does not include viewer-specific data (such as muted, unreadCount, status, lastMessage, lastReaction), since the requester is a moderator and not a member of the conversation. The member list is not included; use chat.bsky.moderation.getConvoMembers to list members. */\nconst convoView = /*#__PURE__*/ l.typedObject<ConvoView>(\n $nsid,\n 'convoView',\n /*#__PURE__*/ l.object({\n id: /*#__PURE__*/ l.string(),\n rev: /*#__PURE__*/ l.string(),\n kind: /*#__PURE__*/ l.optional(\n /*#__PURE__*/ l.typedUnion(\n [\n /*#__PURE__*/ l.typedRef<DirectConvo>((() => directConvo) as any),\n /*#__PURE__*/ l.typedRef<GroupConvo>((() => groupConvo) as any),\n ],\n false,\n ),\n ),\n }),\n)\n\nexport { convoView }\n\n/** Data specific to a direct conversation, for moderation purposes. */\ntype DirectConvo = { $type?: 'chat.bsky.moderation.defs#directConvo' }\n\nexport type { DirectConvo }\n\n/** Data specific to a direct conversation, for moderation purposes. */\nconst directConvo = /*#__PURE__*/ l.typedObject<DirectConvo>(\n $nsid,\n 'directConvo',\n /*#__PURE__*/ l.object({}),\n)\n\nexport { directConvo }\n\n/** Data specific to a group conversation, for moderation purposes. Unlike chat.bsky.convo.defs#groupConvo, it does not include viewer-specific data (such as unreadJoinRequestCount), since the requester is a moderator and not a member of the conversation. */\ntype GroupConvo = {\n $type?: 'chat.bsky.moderation.defs#groupConvo'\n createdAt: l.DatetimeString\n joinLink?: GroupDefs.JoinLinkView\n\n /**\n * The total number of pending join requests for the group conversation. This information is only visible to the owner and to moderators. Capped at 21.\n */\n joinRequestCount: number\n\n /**\n * The lock status of the conversation.\n */\n lockStatus: ConvoDefs.ConvoLockStatus\n\n /**\n * The total number of members in the group conversation.\n */\n memberCount: number\n\n /**\n * The maximum number of members allowed in the group conversation.\n */\n memberLimit: number\n\n /**\n * The display name of the group conversation.\n */\n name: string\n}\n\nexport type { GroupConvo }\n\n/** Data specific to a group conversation, for moderation purposes. Unlike chat.bsky.convo.defs#groupConvo, it does not include viewer-specific data (such as unreadJoinRequestCount), since the requester is a moderator and not a member of the conversation. */\nconst groupConvo = /*#__PURE__*/ l.typedObject<GroupConvo>(\n $nsid,\n 'groupConvo',\n /*#__PURE__*/ l.object({\n createdAt: /*#__PURE__*/ l.string({ format: 'datetime' }),\n joinLink: /*#__PURE__*/ l.optional(\n /*#__PURE__*/ l.ref<GroupDefs.JoinLinkView>(\n (() => GroupDefs.joinLinkView) as any,\n ),\n ),\n joinRequestCount: /*#__PURE__*/ l.integer(),\n lockStatus: /*#__PURE__*/ l.ref<ConvoDefs.ConvoLockStatus>(\n (() => ConvoDefs.convoLockStatus) as any,\n ),\n memberCount: /*#__PURE__*/ l.integer(),\n memberLimit: /*#__PURE__*/ l.integer(),\n name: /*#__PURE__*/ l.string({ maxGraphemes: 50, maxLength: 500 }),\n }),\n)\n\nexport { groupConvo }\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/bsky",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.250",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Reference implementation of app.bsky App View (Bluesky API)",
|
|
6
6
|
"keywords": [
|
|
@@ -49,17 +49,17 @@
|
|
|
49
49
|
"undici": "^8.5.0",
|
|
50
50
|
"zod": "3.23.8",
|
|
51
51
|
"@atproto-labs/fetch-node": "^0.3.3",
|
|
52
|
-
"@atproto
|
|
53
|
-
"@atproto/api": "^0.20.22",
|
|
52
|
+
"@atproto/api": "^0.20.23",
|
|
54
53
|
"@atproto/common": "^0.6.4",
|
|
55
54
|
"@atproto/crypto": "^0.5.2",
|
|
56
|
-
"@atproto/sync": "^0.3.4",
|
|
57
|
-
"@atproto/syntax": "^0.6.3",
|
|
58
|
-
"@atproto/xrpc-server": "^0.11.3",
|
|
59
|
-
"@atproto/lex": "^0.1.5",
|
|
60
55
|
"@atproto/did": "^0.5.2",
|
|
56
|
+
"@atproto/identity": "^0.5.2",
|
|
57
|
+
"@atproto-labs/xrpc-utils": "^0.1.3",
|
|
58
|
+
"@atproto/lex": "^0.1.5",
|
|
61
59
|
"@atproto/repo": "^0.10.2",
|
|
62
|
-
"@atproto/
|
|
60
|
+
"@atproto/sync": "^0.3.5",
|
|
61
|
+
"@atproto/xrpc-server": "^0.11.4",
|
|
62
|
+
"@atproto/syntax": "^0.6.3"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@bufbuild/buf": "^1.28.1",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@types/qs": "^6.9.7",
|
|
74
74
|
"ts-node": "^10.8.2",
|
|
75
75
|
"vitest": "^4.0.16",
|
|
76
|
-
"@atproto/pds": "^0.5.
|
|
76
|
+
"@atproto/pds": "^0.5.11"
|
|
77
77
|
},
|
|
78
78
|
"type": "module",
|
|
79
79
|
"exports": {
|
|
@@ -158,6 +158,25 @@ export const AGE_ASSURANCE_CONFIG = app.bsky.ageassurance.defs.config.$build({
|
|
|
158
158
|
}),
|
|
159
159
|
],
|
|
160
160
|
},
|
|
161
|
+
{
|
|
162
|
+
countryCode: 'US',
|
|
163
|
+
regionCode: 'TX',
|
|
164
|
+
minAccessAge: 18,
|
|
165
|
+
// On-device age verification is permitted here in addition to the
|
|
166
|
+
// third-party (KWS) flow, which is always supported and remains as a
|
|
167
|
+
// fallback for platforms without the native age API (e.g. web) or
|
|
168
|
+
// insufficient device results.
|
|
169
|
+
additionalVerificationMethods: ['device'],
|
|
170
|
+
rules: [
|
|
171
|
+
app.bsky.ageassurance.defs.configRegionRuleIfAssuredOverAge.$build({
|
|
172
|
+
age: 18,
|
|
173
|
+
access: 'full',
|
|
174
|
+
}),
|
|
175
|
+
app.bsky.ageassurance.defs.configRegionRuleDefault.$build({
|
|
176
|
+
access: 'none',
|
|
177
|
+
}),
|
|
178
|
+
],
|
|
179
|
+
},
|
|
161
180
|
{
|
|
162
181
|
countryCode: 'BR',
|
|
163
182
|
regionCode: undefined,
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
parseKWSAgeVerifiedStatus,
|
|
4
|
+
serializeKWSAgeVerifiedStatus,
|
|
5
|
+
} from './age-verified.js'
|
|
6
|
+
|
|
7
|
+
describe('parseKWSAgeVerifiedStatus', () => {
|
|
8
|
+
it('parses a minimal status object', () => {
|
|
9
|
+
const raw = JSON.stringify({ verified: true, verifiedMinimumAge: 18 })
|
|
10
|
+
expect(parseKWSAgeVerifiedStatus(raw)).toEqual({
|
|
11
|
+
verified: true,
|
|
12
|
+
verifiedMinimumAge: 18,
|
|
13
|
+
})
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('parses the optional transactionId and timestamp fields', () => {
|
|
17
|
+
const raw = JSON.stringify({
|
|
18
|
+
verified: true,
|
|
19
|
+
verifiedMinimumAge: 18,
|
|
20
|
+
transactionId: 'txn-123',
|
|
21
|
+
timestamp: 1750680000,
|
|
22
|
+
})
|
|
23
|
+
expect(parseKWSAgeVerifiedStatus(raw)).toEqual({
|
|
24
|
+
verified: true,
|
|
25
|
+
verifiedMinimumAge: 18,
|
|
26
|
+
transactionId: 'txn-123',
|
|
27
|
+
timestamp: 1750680000,
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('tolerates the new timestamp field without transactionId', () => {
|
|
32
|
+
const raw = JSON.stringify({
|
|
33
|
+
verified: true,
|
|
34
|
+
verifiedMinimumAge: 18,
|
|
35
|
+
timestamp: 1750680000,
|
|
36
|
+
})
|
|
37
|
+
expect(parseKWSAgeVerifiedStatus(raw)).toEqual({
|
|
38
|
+
verified: true,
|
|
39
|
+
verifiedMinimumAge: 18,
|
|
40
|
+
timestamp: 1750680000,
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('silently strips unknown fields KWS may add', () => {
|
|
45
|
+
const raw = JSON.stringify({
|
|
46
|
+
verified: true,
|
|
47
|
+
verifiedMinimumAge: 18,
|
|
48
|
+
timestamp: 1750680000,
|
|
49
|
+
errorCode: null,
|
|
50
|
+
somethingNew: 'ignored',
|
|
51
|
+
})
|
|
52
|
+
expect(parseKWSAgeVerifiedStatus(raw)).toEqual({
|
|
53
|
+
verified: true,
|
|
54
|
+
verifiedMinimumAge: 18,
|
|
55
|
+
timestamp: 1750680000,
|
|
56
|
+
errorCode: null,
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('strips a truly unknown field', () => {
|
|
61
|
+
const raw = JSON.stringify({
|
|
62
|
+
verified: true,
|
|
63
|
+
verifiedMinimumAge: 18,
|
|
64
|
+
somethingNew: 'ignored',
|
|
65
|
+
})
|
|
66
|
+
expect(parseKWSAgeVerifiedStatus(raw)).toEqual({
|
|
67
|
+
verified: true,
|
|
68
|
+
verifiedMinimumAge: 18,
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('throws on malformed JSON', () => {
|
|
73
|
+
expect(() => parseKWSAgeVerifiedStatus('not json')).toThrow(
|
|
74
|
+
/Invalid KWS age-verified status/,
|
|
75
|
+
)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('throws when a required field is missing', () => {
|
|
79
|
+
const raw = JSON.stringify({ verified: true })
|
|
80
|
+
expect(() => parseKWSAgeVerifiedStatus(raw)).toThrow(
|
|
81
|
+
/Invalid KWS age-verified status/,
|
|
82
|
+
)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
describe('serializeKWSAgeVerifiedStatus', () => {
|
|
87
|
+
it('round-trips a status object including timestamp', () => {
|
|
88
|
+
const status = {
|
|
89
|
+
verified: true,
|
|
90
|
+
verifiedMinimumAge: 18,
|
|
91
|
+
transactionId: 'txn-123',
|
|
92
|
+
timestamp: 1750680000,
|
|
93
|
+
}
|
|
94
|
+
expect(
|
|
95
|
+
parseKWSAgeVerifiedStatus(serializeKWSAgeVerifiedStatus(status)),
|
|
96
|
+
).toEqual(status)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
@@ -7,6 +7,12 @@ export const KWSAgeVerifiedStatusSchema = z.object({
|
|
|
7
7
|
verified: z.boolean(),
|
|
8
8
|
verifiedMinimumAge: z.number(),
|
|
9
9
|
transactionId: z.string().optional(),
|
|
10
|
+
// A code indicating the reason for failure, or null when verification
|
|
11
|
+
// succeeded.
|
|
12
|
+
errorCode: z.string().nullable().optional(),
|
|
13
|
+
// Epoch seconds indicating when KWS generated the payload. Optional so we
|
|
14
|
+
// remain compatible with payloads sent before this field was introduced.
|
|
15
|
+
timestamp: z.number().optional(),
|
|
10
16
|
})
|
|
11
17
|
|
|
12
18
|
/**
|
|
@@ -11,6 +11,9 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
11
11
|
const requester = auth.credentials.iss
|
|
12
12
|
const [did] = await ctx.hydrator.actor.getDids([actor])
|
|
13
13
|
if (!did) throw new InvalidRequestError('Actor not found')
|
|
14
|
+
if (requester === did) {
|
|
15
|
+
throw new InvalidRequestError('Actor cannot mute themselves')
|
|
16
|
+
}
|
|
14
17
|
await ctx.bsyncClient.addMuteOperation({
|
|
15
18
|
type: MuteOperation_Type.ADD,
|
|
16
19
|
actorDid: requester,
|
package/src/api/kws/types.ts
CHANGED
|
@@ -25,6 +25,14 @@ export const externalPayloadSchema = z
|
|
|
25
25
|
|
|
26
26
|
export type KwsStatus = {
|
|
27
27
|
verified: boolean
|
|
28
|
+
// A unique reference ID for the verification transaction.
|
|
29
|
+
transactionId?: string
|
|
30
|
+
// A code indicating the reason for failure, or null when verification
|
|
31
|
+
// succeeded.
|
|
32
|
+
errorCode?: string | null
|
|
33
|
+
// Epoch seconds indicating when KWS generated the payload. Optional so we
|
|
34
|
+
// remain compatible with payloads sent before this field was introduced.
|
|
35
|
+
timestamp?: number
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
export type KwsVerificationIntermediateQuery = {
|
|
@@ -56,6 +64,9 @@ export type KwsWebhookBody = {
|
|
|
56
64
|
// Not `.strict()` to avoid breaking if KWS adds fields.
|
|
57
65
|
export const statusSchema = z.object({
|
|
58
66
|
verified: z.boolean(),
|
|
67
|
+
transactionId: z.string().optional(),
|
|
68
|
+
errorCode: z.string().nullable().optional(),
|
|
69
|
+
timestamp: z.number().optional(),
|
|
59
70
|
})
|
|
60
71
|
|
|
61
72
|
// Not `.strict()` to avoid breaking if KWS adds fields.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { parseStatus } from './util.js'
|
|
3
|
+
|
|
4
|
+
describe('parseStatus', () => {
|
|
5
|
+
it('parses a minimal status object', () => {
|
|
6
|
+
const raw = JSON.stringify({ verified: true })
|
|
7
|
+
expect(parseStatus(raw)).toEqual({ verified: true })
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('parses the optional transactionId, errorCode, and timestamp fields', () => {
|
|
11
|
+
const raw = JSON.stringify({
|
|
12
|
+
verified: true,
|
|
13
|
+
transactionId: 'txn-123',
|
|
14
|
+
errorCode: null,
|
|
15
|
+
timestamp: 1750680000,
|
|
16
|
+
})
|
|
17
|
+
expect(parseStatus(raw)).toEqual({
|
|
18
|
+
verified: true,
|
|
19
|
+
transactionId: 'txn-123',
|
|
20
|
+
errorCode: null,
|
|
21
|
+
timestamp: 1750680000,
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('parses a non-null errorCode', () => {
|
|
26
|
+
const raw = JSON.stringify({ verified: false, errorCode: 'some-failure' })
|
|
27
|
+
expect(parseStatus(raw)).toEqual({
|
|
28
|
+
verified: false,
|
|
29
|
+
errorCode: 'some-failure',
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('silently strips unknown fields KWS may add', () => {
|
|
34
|
+
const raw = JSON.stringify({
|
|
35
|
+
verified: true,
|
|
36
|
+
timestamp: 1750680000,
|
|
37
|
+
somethingNew: 'ignored',
|
|
38
|
+
})
|
|
39
|
+
expect(parseStatus(raw)).toEqual({ verified: true, timestamp: 1750680000 })
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('throws on malformed JSON', () => {
|
|
43
|
+
expect(() => parseStatus('not json')).toThrow(/Invalid status/)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('throws when verified is missing', () => {
|
|
47
|
+
const raw = JSON.stringify({ timestamp: 1750680000 })
|
|
48
|
+
expect(() => parseStatus(raw)).toThrow(/Invalid status/)
|
|
49
|
+
})
|
|
50
|
+
})
|
package/src/data-plane/index.ts
CHANGED