@atproto/bsky 0.0.249 → 0.0.251
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 +30 -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/feed/getFeed.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getFeed.js +4 -0
- package/dist/api/app/bsky/feed/getFeed.js.map +1 -1
- package/dist/api/app/bsky/feed/searchPosts.js +5 -0
- package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
- package/dist/api/app/bsky/feed/searchPostsV2.js +5 -0
- package/dist/api/app/bsky/feed/searchPostsV2.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/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -0
- package/dist/config.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/hydration/feed.d.ts +1 -2
- package/dist/hydration/feed.d.ts.map +1 -1
- package/dist/hydration/feed.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 +10 -10
- 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/feed/getFeed.ts +5 -0
- package/src/api/app/bsky/feed/searchPosts.ts +7 -0
- package/src/api/app/bsky/feed/searchPostsV2.ts +7 -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/config.ts +9 -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/src/hydration/feed.ts +1 -2
- package/tests/__snapshots__/feed-generation.test.ts.snap +62 -15
- package/tests/data-plane/thread-mutes.test.ts +2 -0
- package/tests/feed-generation.test.ts +55 -9
- 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/tests/views/post-search.test.ts +81 -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
|
@@ -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
|
/**
|
|
@@ -251,6 +251,11 @@ const skeletonFromFeedGen = async (
|
|
|
251
251
|
app.bsky.feed.defs.skeletonReasonRepost.$isTypeOf(item.reason)
|
|
252
252
|
? { uri: item.reason.repost }
|
|
253
253
|
: undefined,
|
|
254
|
+
authorPinned:
|
|
255
|
+
item.reason != null &&
|
|
256
|
+
app.bsky.feed.defs.skeletonReasonPin.$isTypeOf(item.reason)
|
|
257
|
+
? true
|
|
258
|
+
: undefined,
|
|
254
259
|
feedContext: item.feedContext,
|
|
255
260
|
}))
|
|
256
261
|
|
|
@@ -196,6 +196,13 @@ const noBlocksOrTagged = (inputs: RulesFnInput<Context, Params, Skeleton>) => {
|
|
|
196
196
|
// Cases to never show.
|
|
197
197
|
if (ctx.views.viewerBlockExists(creator, hydration)) return false
|
|
198
198
|
|
|
199
|
+
// Tags that are hidden from all search surfaces (Top and Latest),
|
|
200
|
+
// regardless of curation or author filtering.
|
|
201
|
+
const alwaysHidden = [...ctx.cfg.searchTagsHideAll].some((t) =>
|
|
202
|
+
post.tags.has(t),
|
|
203
|
+
)
|
|
204
|
+
if (alwaysHidden) return false
|
|
205
|
+
|
|
199
206
|
let tagged = false
|
|
200
207
|
if (
|
|
201
208
|
params.hydrateCtx.features?.checkGate(
|
|
@@ -164,6 +164,13 @@ const noBlocksOrTagged = (inputs: RulesFnInput<Context, Params, Skeleton>) => {
|
|
|
164
164
|
|
|
165
165
|
if (ctx.views.viewerBlockExists(creator, hydration)) return false
|
|
166
166
|
|
|
167
|
+
// Tags that are hidden from all search surfaces (Top and Latest),
|
|
168
|
+
// regardless of curation or author filtering.
|
|
169
|
+
const alwaysHidden = [...ctx.cfg.searchTagsHideAll].some((t) =>
|
|
170
|
+
post.tags.has(t),
|
|
171
|
+
)
|
|
172
|
+
if (alwaysHidden) return false
|
|
173
|
+
|
|
167
174
|
const tagged = [...ctx.cfg.searchTagsHide].some((t) => post.tags.has(t))
|
|
168
175
|
|
|
169
176
|
if (isCuratedSearch && tagged) return false
|
|
@@ -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/config.ts
CHANGED
|
@@ -63,6 +63,7 @@ export interface ServerConfigValues {
|
|
|
63
63
|
rolodexIgnoreBadTls?: boolean
|
|
64
64
|
searchUrl?: string
|
|
65
65
|
searchTagsHide: Set<string>
|
|
66
|
+
searchTagsHideAll: Set<string>
|
|
66
67
|
suggestionsUrl?: string
|
|
67
68
|
suggestionsApiKey?: string
|
|
68
69
|
topicsUrl?: string
|
|
@@ -163,6 +164,9 @@ export class ServerConfig {
|
|
|
163
164
|
process.env.BSKY_SEARCH_ENDPOINT ||
|
|
164
165
|
undefined
|
|
165
166
|
const searchTagsHide = new Set(envList(process.env.BSKY_SEARCH_TAGS_HIDE))
|
|
167
|
+
const searchTagsHideAll = new Set(
|
|
168
|
+
envList(process.env.BSKY_SEARCH_TAGS_HIDE_ALL),
|
|
169
|
+
)
|
|
166
170
|
const suggestionsUrl = process.env.BSKY_SUGGESTIONS_URL || undefined
|
|
167
171
|
const suggestionsApiKey = process.env.BSKY_SUGGESTIONS_API_KEY || undefined
|
|
168
172
|
const topicsUrl = process.env.BSKY_TOPICS_URL || undefined
|
|
@@ -354,6 +358,7 @@ export class ServerConfig {
|
|
|
354
358
|
dataplaneIgnoreBadTls,
|
|
355
359
|
searchUrl,
|
|
356
360
|
searchTagsHide,
|
|
361
|
+
searchTagsHideAll,
|
|
357
362
|
suggestionsUrl,
|
|
358
363
|
suggestionsApiKey,
|
|
359
364
|
topicsUrl,
|
|
@@ -524,6 +529,10 @@ export class ServerConfig {
|
|
|
524
529
|
return this.cfg.searchTagsHide
|
|
525
530
|
}
|
|
526
531
|
|
|
532
|
+
get searchTagsHideAll() {
|
|
533
|
+
return this.cfg.searchTagsHideAll
|
|
534
|
+
}
|
|
535
|
+
|
|
527
536
|
get suggestionsUrl() {
|
|
528
537
|
return this.cfg.suggestionsUrl
|
|
529
538
|
}
|
package/src/data-plane/index.ts
CHANGED