@atproto/bsky 0.0.207 → 0.0.209

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/bsky",
3
- "version": "0.0.207",
3
+ "version": "0.0.209",
4
4
  "license": "MIT",
5
5
  "description": "Reference implementation of app.bsky App View (Bluesky API)",
6
6
  "keywords": [
@@ -52,8 +52,8 @@
52
52
  "undici": "^6.19.8",
53
53
  "zod": "3.23.8",
54
54
  "@atproto-labs/fetch-node": "0.2.0",
55
- "@atproto/common": "^0.5.6",
56
- "@atproto/api": "^0.18.12",
55
+ "@atproto/api": "^0.18.13",
56
+ "@atproto/common": "^0.5.7",
57
57
  "@atproto-labs/xrpc-utils": "0.0.24",
58
58
  "@atproto/crypto": "^0.4.5",
59
59
  "@atproto/did": "^0.2.4",
@@ -62,7 +62,7 @@
62
62
  "@atproto/repo": "^0.8.12",
63
63
  "@atproto/sync": "^0.1.39",
64
64
  "@atproto/syntax": "^0.4.2",
65
- "@atproto/xrpc-server": "^0.10.7"
65
+ "@atproto/xrpc-server": "^0.10.8"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@bufbuild/buf": "^1.28.1",
@@ -78,9 +78,9 @@
78
78
  "jest": "^28.1.2",
79
79
  "ts-node": "^10.8.2",
80
80
  "typescript": "^5.6.3",
81
- "@atproto/api": "^0.18.12",
81
+ "@atproto/api": "^0.18.13",
82
82
  "@atproto/lex-cli": "^0.9.8",
83
- "@atproto/pds": "^0.4.202",
83
+ "@atproto/pds": "^0.4.203",
84
84
  "@atproto/xrpc": "^0.7.7"
85
85
  },
86
86
  "scripts": {
@@ -1,13 +1,13 @@
1
1
  import { Router } from 'express'
2
+ import { didWebToUrl, isDidWeb } from '@atproto/did'
2
3
  import { AppContext } from '../context'
3
4
 
4
5
  export const createRouter = (ctx: AppContext): Router => {
5
6
  const router = Router()
6
7
 
7
8
  const did = ctx.cfg.serverDid
8
- if (did.startsWith('did:web:')) {
9
- const hostname = did.slice('did:web:'.length)
10
- const serviceEndpoint = `https://${hostname}`
9
+ if (isDidWeb(did)) {
10
+ const serviceEndpoint = didWebToUrl(did).origin
11
11
 
12
12
  router.get('/.well-known/did.json', (_req, res) => {
13
13
  res.json({
@@ -160,20 +160,10 @@ export class ActorHydrator {
160
160
  dids: string[],
161
161
  opts: {
162
162
  includeTakedowns?: boolean
163
- /**
164
- * The raw `HydrationCtx.includeTakedowns` value, independent of any
165
- * special casing that may apply to `includeTakedowns` within this
166
- * method.
167
- */
168
- includeTakedownsBase?: boolean
169
163
  skipCacheForDids?: string[]
170
164
  } = {},
171
165
  ): Promise<Actors> {
172
- const {
173
- includeTakedowns = false,
174
- skipCacheForDids,
175
- includeTakedownsBase = false,
176
- } = opts
166
+ const { includeTakedowns = false, skipCacheForDids } = opts
177
167
  if (!dids.length) return new HydrationMap<Actor>()
178
168
  const res = await this.dataplane.getActors({ dids, skipCacheForDids })
179
169
  return dids.reduce((acc, did, i) => {
@@ -194,7 +184,14 @@ export class ActorHydrator {
194
184
  : undefined
195
185
 
196
186
  const status = actor.statusRecord
197
- ? parseRecord<StatusRecord>(actor.statusRecord, includeTakedownsBase)
187
+ ? parseRecord<StatusRecord>(
188
+ actor.statusRecord,
189
+ /*
190
+ * Always true, we filter this out in the `Views.status()`. If we
191
+ * ever remove that filter, we'll want to reinstate this here.
192
+ */
193
+ true,
194
+ )
198
195
  : undefined
199
196
 
200
197
  const verifications = mapDefined(
@@ -234,11 +234,9 @@ export class Hydrator {
234
234
  */
235
235
  const includeTakedowns =
236
236
  ctx.includeTakedowns || ctx.overrideIncludeTakedownsForActor
237
- const includeTakedownsBase = ctx.includeTakedowns
238
237
  const [actors, labels, profileViewersState] = await Promise.all([
239
238
  this.actor.getActors(dids, {
240
239
  includeTakedowns,
241
- includeTakedownsBase,
242
240
  skipCacheForDids: ctx.skipCacheForViewer,
243
241
  }),
244
242
  this.label.getLabelsForSubjects(labelSubjectsForDid(dids), ctx.labelers),
@@ -844,6 +844,11 @@ export const schemaDict = {
844
844
  description:
845
845
  'True if the status is not expired, false if it is expired. Only present if expiration was set.',
846
846
  },
847
+ isDisabled: {
848
+ type: 'boolean',
849
+ description:
850
+ "True if the user's go-live access has been disabled by a moderator, false otherwise.",
851
+ },
847
852
  },
848
853
  },
849
854
  },
@@ -662,6 +662,8 @@ export interface StatusView {
662
662
  expiresAt?: string
663
663
  /** True if the status is not expired, false if it is expired. Only present if expiration was set. */
664
664
  isActive?: boolean
665
+ /** True if the user's go-live access has been disabled by a moderator, false otherwise. */
666
+ isDisabled?: boolean
665
667
  }
666
668
 
667
669
  const hashStatusView = 'statusView'
@@ -544,8 +544,20 @@ export class Views {
544
544
  const actor = state.actors?.get(did)
545
545
  if (!actor?.status) return
546
546
 
547
+ const isViewerStatusOwner = did === state.ctx?.viewer
547
548
  const { status } = actor
548
- const { record, sortedAt, cid } = status
549
+ const { record, sortedAt, cid, takedownRef } = status
550
+ const isTakenDown = !!takedownRef
551
+
552
+ /*
553
+ * Manual filter for takendown status records. If this is ever removed, we
554
+ * need to reinstate `includeTakedowns` handling in the `Actor.getActors`
555
+ * hydrator.
556
+ */
557
+ if (isTakenDown && !isViewerStatusOwner) {
558
+ return undefined
559
+ }
560
+
549
561
  const uri = AtUri.make(did, ids.AppBskyActorStatus, 'self').toString()
550
562
 
551
563
  const minDuration = 5 * MINUTE
@@ -564,7 +576,7 @@ export class Views {
564
576
 
565
577
  const isActive = expiresAtMs ? expiresAtMs > Date.now() : undefined
566
578
 
567
- return {
579
+ const response: StatusView = {
568
580
  uri,
569
581
  cid,
570
582
  record: record,
@@ -575,6 +587,12 @@ export class Views {
575
587
  expiresAt,
576
588
  isActive,
577
589
  }
590
+
591
+ if (isViewerStatusOwner) {
592
+ response.isDisabled = isTakenDown
593
+ }
594
+
595
+ return response
578
596
  }
579
597
 
580
598
  blockedProfileViewer(
@@ -714,6 +714,7 @@ Object {
714
714
  },
715
715
  "expiresAt": "1970-01-01T00:00:00.000Z",
716
716
  "isActive": true,
717
+ "isDisabled": false,
717
718
  "record": Object {
718
719
  "$type": "app.bsky.actor.status",
719
720
  "createdAt": "1970-01-01T00:00:00.000Z",
@@ -746,6 +747,40 @@ Object {
746
747
  },
747
748
  "expiresAt": "1970-01-01T00:00:00.000Z",
748
749
  "isActive": false,
750
+ "isDisabled": false,
751
+ "record": Object {
752
+ "$type": "app.bsky.actor.status",
753
+ "createdAt": "1970-01-01T00:00:00.000Z",
754
+ "durationMinutes": 10,
755
+ "embed": Object {
756
+ "$type": "app.bsky.embed.external",
757
+ "external": Object {
758
+ "description": "testLink",
759
+ "title": "TestImage",
760
+ "uri": "https://example.com",
761
+ },
762
+ },
763
+ "status": "app.bsky.actor.status#live",
764
+ },
765
+ "status": "app.bsky.actor.status#live",
766
+ "uri": "record(0)",
767
+ }
768
+ `;
769
+
770
+ exports[`pds profile views status when taken down it returns the live status with isDisabled=true for status owner 1`] = `
771
+ Object {
772
+ "cid": "cids(0)",
773
+ "embed": Object {
774
+ "$type": "app.bsky.embed.external#view",
775
+ "external": Object {
776
+ "description": "testLink",
777
+ "title": "TestImage",
778
+ "uri": "https://example.com",
779
+ },
780
+ },
781
+ "expiresAt": "1970-01-01T00:00:00.000Z",
782
+ "isActive": true,
783
+ "isDisabled": true,
749
784
  "record": Object {
750
785
  "$type": "app.bsky.actor.status",
751
786
  "createdAt": "1970-01-01T00:00:00.000Z",
@@ -495,7 +495,7 @@ describe('pds profile views', () => {
495
495
  })
496
496
 
497
497
  describe('when taken down', () => {
498
- it('it does not return the live status', async () => {
498
+ beforeAll(async () => {
499
499
  const res = await sc.agent.com.atproto.repo.putRecord(
500
500
  {
501
501
  repo: alice,
@@ -519,7 +519,9 @@ describe('pds profile views', () => {
519
519
  recordUri: res.data.uri,
520
520
  })
521
521
  await network.processAll()
522
+ })
522
523
 
524
+ it('it returns the live status with isDisabled=true for status owner', async () => {
523
525
  const { data } = await agent.api.app.bsky.actor.getProfile(
524
526
  { actor: alice },
525
527
  {
@@ -530,6 +532,21 @@ describe('pds profile views', () => {
530
532
  },
531
533
  )
532
534
 
535
+ expect(data.status?.isDisabled).toBe(true)
536
+ expect(forSnapshot(data.status)).toMatchSnapshot()
537
+ })
538
+
539
+ it('it does not return the live status for non-owner', async () => {
540
+ const { data } = await agent.api.app.bsky.actor.getProfile(
541
+ { actor: alice },
542
+ {
543
+ headers: await network.serviceHeaders(
544
+ bob,
545
+ ids.AppBskyActorGetProfile,
546
+ ),
547
+ },
548
+ )
549
+
533
550
  expect(forSnapshot(data.status)).toBeUndefined()
534
551
  })
535
552
  })