@atproto/api 0.6.15 → 0.6.17

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.
@@ -1,2 +1,2 @@
1
1
  import { ModerationSubjectFeedGenerator, ModerationDecision, ModerationOpts } from '../types';
2
- export declare function decideFeedGenerator(subject: ModerationSubjectFeedGenerator, opts: ModerationOpts): ModerationDecision;
2
+ export declare function decideFeedGenerator(_subject: ModerationSubjectFeedGenerator, _opts: ModerationOpts): ModerationDecision;
@@ -1,2 +1,2 @@
1
1
  import { ModerationSubjectUserList, ModerationOpts, ModerationDecision } from '../types';
2
- export declare function decideUserList(subject: ModerationSubjectUserList, opts: ModerationOpts): ModerationDecision;
2
+ export declare function decideUserList(_subject: ModerationSubjectUserList, _opts: ModerationOpts): ModerationDecision;
package/dist/types.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { LabelPreference } from './moderation/types';
2
- import { AppBskyActorDefs } from './client';
3
2
  export declare type AtpSessionEvent = 'create' | 'create-failed' | 'update' | 'expired';
4
3
  export interface AtpSessionData {
5
4
  refreshJwt: string;
@@ -34,13 +33,26 @@ export interface AtpAgentGlobalOpts {
34
33
  fetch: AtpAgentFetchHandler;
35
34
  }
36
35
  export declare type BskyLabelPreference = LabelPreference | 'show';
36
+ export interface BskyFeedViewPreference {
37
+ hideReplies: boolean;
38
+ hideRepliesByUnfollowed: boolean;
39
+ hideRepliesByLikeCount: number;
40
+ hideReposts: boolean;
41
+ hideQuotePosts: boolean;
42
+ [key: string]: any;
43
+ }
44
+ export interface BskyThreadViewPreference {
45
+ sort: string;
46
+ prioritizeFollowedUsers: boolean;
47
+ [key: string]: any;
48
+ }
37
49
  export interface BskyPreferences {
38
50
  feeds: {
39
51
  saved?: string[];
40
52
  pinned?: string[];
41
53
  };
42
- feedViewPrefs: Record<string, Omit<AppBskyActorDefs.FeedViewPref, '$type'>>;
43
- threadViewPrefs: Omit<AppBskyActorDefs.ThreadViewPref, '$type'>;
54
+ feedViewPrefs: Record<string, BskyFeedViewPreference>;
55
+ threadViewPrefs: BskyThreadViewPreference;
44
56
  adultContentEnabled: boolean;
45
57
  contentLabels: Record<string, BskyLabelPreference>;
46
58
  birthDate: Date | undefined;
package/package.json CHANGED
@@ -1,13 +1,20 @@
1
1
  {
2
2
  "name": "@atproto/api",
3
- "version": "0.6.15",
4
- "main": "dist/index.js",
3
+ "version": "0.6.17",
5
4
  "license": "MIT",
5
+ "description": "Client library for atproto and Bluesky",
6
+ "keywords": [
7
+ "atproto",
8
+ "bluesky",
9
+ "api"
10
+ ],
11
+ "homepage": "https://atproto.com",
6
12
  "repository": {
7
13
  "type": "git",
8
- "url": "https://github.com/bluesky-social/atproto.git",
14
+ "url": "https://github.com/bluesky-social/atproto",
9
15
  "directory": "packages/api"
10
16
  },
17
+ "main": "dist/index.js",
11
18
  "dependencies": {
12
19
  "multiformats": "^9.9.0",
13
20
  "tlds": "^1.234.0",
@@ -20,7 +27,7 @@
20
27
  "devDependencies": {
21
28
  "common-tags": "^1.8.2",
22
29
  "@atproto/lex-cli": "^0.2.1",
23
- "@atproto/pds": "^0.1.15"
30
+ "@atproto/pds": "^0.1.17"
24
31
  },
25
32
  "scripts": {
26
33
  "codegen": "pnpm docgen && node ./scripts/generate-code.mjs && lex gen-api ./src/client ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/*",
package/src/agent.ts CHANGED
@@ -7,7 +7,6 @@ import {
7
7
  ComAtprotoServerCreateSession,
8
8
  ComAtprotoServerGetSession,
9
9
  ComAtprotoServerRefreshSession,
10
- ComAtprotoRepoUploadBlob,
11
10
  } from './client'
12
11
  import {
13
12
  AtpSessionData,
package/src/bsky-agent.ts CHANGED
@@ -6,7 +6,12 @@ import {
6
6
  AppBskyActorDefs,
7
7
  ComAtprotoRepoPutRecord,
8
8
  } from './client'
9
- import { BskyPreferences, BskyLabelPreference } from './types'
9
+ import {
10
+ BskyPreferences,
11
+ BskyLabelPreference,
12
+ BskyFeedViewPreference,
13
+ BskyThreadViewPreference,
14
+ } from './types'
10
15
 
11
16
  const FEED_VIEW_PREF_DEFAULTS = {
12
17
  hideReplies: false,
@@ -311,12 +316,14 @@ export class BskyAgent extends AtpAgent {
311
316
  AppBskyActorDefs.isFeedViewPref(pref) &&
312
317
  AppBskyActorDefs.validateFeedViewPref(pref).success
313
318
  ) {
319
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
314
320
  const { $type, feed, ...v } = pref
315
321
  prefs.feedViewPrefs[pref.feed] = { ...FEED_VIEW_PREF_DEFAULTS, ...v }
316
322
  } else if (
317
323
  AppBskyActorDefs.isThreadViewPref(pref) &&
318
324
  AppBskyActorDefs.validateThreadViewPref(pref).success
319
325
  ) {
326
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
320
327
  const { $type, ...v } = pref
321
328
  prefs.threadViewPrefs = { ...prefs.threadViewPrefs, ...v }
322
329
  }
@@ -437,10 +444,7 @@ export class BskyAgent extends AtpAgent {
437
444
  })
438
445
  }
439
446
 
440
- async setFeedViewPrefs(
441
- feed: string,
442
- pref: Omit<AppBskyActorDefs.FeedViewPref, '$type' | 'feed'>,
443
- ) {
447
+ async setFeedViewPrefs(feed: string, pref: Partial<BskyFeedViewPreference>) {
444
448
  await updatePreferences(this, (prefs: AppBskyActorDefs.Preferences) => {
445
449
  const existing = prefs.findLast(
446
450
  (pref) =>
@@ -459,9 +463,7 @@ export class BskyAgent extends AtpAgent {
459
463
  })
460
464
  }
461
465
 
462
- async setThreadViewPrefs(
463
- pref: Omit<AppBskyActorDefs.ThreadViewPref, '$type'>,
464
- ) {
466
+ async setThreadViewPrefs(pref: Partial<BskyThreadViewPreference>) {
465
467
  await updatePreferences(this, (prefs: AppBskyActorDefs.Preferences) => {
466
468
  const existing = prefs.findLast(
467
469
  (pref) =>
@@ -5622,6 +5622,16 @@ export const schemaDict = {
5622
5622
  type: 'union',
5623
5623
  refs: ['lex:com.atproto.label.defs#selfLabels'],
5624
5624
  },
5625
+ tags: {
5626
+ type: 'array',
5627
+ maxLength: 8,
5628
+ items: {
5629
+ type: 'string',
5630
+ maxLength: 640,
5631
+ maxGraphemes: 64,
5632
+ },
5633
+ description: 'Additional non-inline tags describing this post.',
5634
+ },
5625
5635
  createdAt: {
5626
5636
  type: 'string',
5627
5637
  format: 'datetime',
@@ -6761,6 +6771,7 @@ export const schemaDict = {
6761
6771
  refs: [
6762
6772
  'lex:app.bsky.richtext.facet#mention',
6763
6773
  'lex:app.bsky.richtext.facet#link',
6774
+ 'lex:app.bsky.richtext.facet#tag',
6764
6775
  ],
6765
6776
  },
6766
6777
  },
@@ -6788,6 +6799,18 @@ export const schemaDict = {
6788
6799
  },
6789
6800
  },
6790
6801
  },
6802
+ tag: {
6803
+ type: 'object',
6804
+ description: 'A hashtag.',
6805
+ required: ['tag'],
6806
+ properties: {
6807
+ tag: {
6808
+ type: 'string',
6809
+ maxLength: 640,
6810
+ maxGraphemes: 64,
6811
+ },
6812
+ },
6813
+ },
6791
6814
  byteSlice: {
6792
6815
  type: 'object',
6793
6816
  description:
@@ -6838,7 +6861,8 @@ export const schemaDict = {
6838
6861
  defs: {
6839
6862
  main: {
6840
6863
  type: 'query',
6841
- description: 'An unspecced view of globally popular items',
6864
+ description:
6865
+ 'DEPRECATED: will be removed soon, please find a feed generator alternative',
6842
6866
  parameters: {
6843
6867
  type: 'params',
6844
6868
  properties: {
@@ -29,6 +29,8 @@ export interface Record {
29
29
  labels?:
30
30
  | ComAtprotoLabelDefs.SelfLabels
31
31
  | { $type: string; [k: string]: unknown }
32
+ /** Additional non-inline tags describing this post. */
33
+ tags?: string[]
32
34
  createdAt: string
33
35
  [k: string]: unknown
34
36
  }
@@ -8,7 +8,7 @@ import { CID } from 'multiformats/cid'
8
8
 
9
9
  export interface Main {
10
10
  index: ByteSlice
11
- features: (Mention | Link | { $type: string; [k: string]: unknown })[]
11
+ features: (Mention | Link | Tag | { $type: string; [k: string]: unknown })[]
12
12
  [k: string]: unknown
13
13
  }
14
14
 
@@ -61,6 +61,22 @@ export function validateLink(v: unknown): ValidationResult {
61
61
  return lexicons.validate('app.bsky.richtext.facet#link', v)
62
62
  }
63
63
 
64
+ /** A hashtag. */
65
+ export interface Tag {
66
+ tag: string
67
+ [k: string]: unknown
68
+ }
69
+
70
+ export function isTag(v: unknown): v is Tag {
71
+ return (
72
+ isObj(v) && hasProp(v, '$type') && v.$type === 'app.bsky.richtext.facet#tag'
73
+ )
74
+ }
75
+
76
+ export function validateTag(v: unknown): ValidationResult {
77
+ return lexicons.validate('app.bsky.richtext.facet#tag', v)
78
+ }
79
+
64
80
  /** A text segment. Start is inclusive, end is exclusive. Indices are for utf8-encoded strings. */
65
81
  export interface ByteSlice {
66
82
  byteStart: number
@@ -5,8 +5,8 @@ import {
5
5
  } from '../types'
6
6
 
7
7
  export function decideFeedGenerator(
8
- subject: ModerationSubjectFeedGenerator,
9
- opts: ModerationOpts,
8
+ _subject: ModerationSubjectFeedGenerator,
9
+ _opts: ModerationOpts,
10
10
  ): ModerationDecision {
11
11
  // TODO handle labels applied on the feed generator itself
12
12
  return ModerationDecision.noop()
@@ -5,8 +5,8 @@ import {
5
5
  } from '../types'
6
6
 
7
7
  export function decideUserList(
8
- subject: ModerationSubjectUserList,
9
- opts: ModerationOpts,
8
+ _subject: ModerationSubjectUserList,
9
+ _opts: ModerationOpts,
10
10
  ): ModerationDecision {
11
11
  // TODO handle labels applied on the list itself
12
12
  return ModerationDecision.noop()
@@ -1,8 +1,4 @@
1
- import {
2
- AppBskyEmbedRecord,
3
- AppBskyEmbedRecordWithMedia,
4
- AppBskyFeedPost,
5
- } from '../client'
1
+ import { AppBskyEmbedRecord, AppBskyEmbedRecordWithMedia } from '../client'
6
2
  import { ModerationDecision, ModerationUI } from './types'
7
3
 
8
4
  export function takeHighestPriorityDecision(
@@ -1,6 +1,8 @@
1
1
  import { RichText } from './rich-text'
2
2
  import { UnicodeString } from './unicode'
3
3
 
4
+ // this regex is intentionally matching on the zero-with-separator codepoint
5
+ // eslint-disable-next-line no-misleading-character-class
4
6
  const EXCESS_SPACE_RE = /[\r\n]([\u00AD\u2060\u200D\u200C\u200B\s]*[\r\n]){2,}/
5
7
  const REPLACEMENT_STR = '\n\n'
6
8
 
package/src/types.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { LabelPreference } from './moderation/types'
2
- import { AppBskyActorDefs } from './client'
3
2
 
4
3
  /**
5
4
  * Used by the PersistSessionHandler to indicate what change occurred
@@ -81,15 +80,37 @@ export type BskyLabelPreference = LabelPreference | 'show'
81
80
  // TEMP we need to permanently convert 'show' to 'ignore', for now we manually convert -prf
82
81
 
83
82
  /**
84
- * Bluesky preferences object
83
+ * Bluesky feed view preferences
84
+ */
85
+
86
+ export interface BskyFeedViewPreference {
87
+ hideReplies: boolean
88
+ hideRepliesByUnfollowed: boolean
89
+ hideRepliesByLikeCount: number
90
+ hideReposts: boolean
91
+ hideQuotePosts: boolean
92
+ [key: string]: any
93
+ }
94
+
95
+ /**
96
+ * Bluesky thread view preferences
97
+ */
98
+ export interface BskyThreadViewPreference {
99
+ sort: string
100
+ prioritizeFollowedUsers: boolean
101
+ [key: string]: any
102
+ }
103
+
104
+ /**
105
+ * Bluesky preferences
85
106
  */
86
107
  export interface BskyPreferences {
87
108
  feeds: {
88
109
  saved?: string[]
89
110
  pinned?: string[]
90
111
  }
91
- feedViewPrefs: Record<string, Omit<AppBskyActorDefs.FeedViewPref, '$type'>>
92
- threadViewPrefs: Omit<AppBskyActorDefs.ThreadViewPref, '$type'>
112
+ feedViewPrefs: Record<string, BskyFeedViewPreference>
113
+ threadViewPrefs: BskyThreadViewPreference
93
114
  adultContentEnabled: boolean
94
115
  contentLabels: Record<string, BskyLabelPreference>
95
116
  birthDate: Date | undefined
@@ -197,7 +197,7 @@ describe('agent', () => {
197
197
 
198
198
  // put the agent through the auth flow
199
199
  AtpAgent.configure({ fetch: tokenExpiredFetchHandler })
200
- const res1 = await agent.api.app.bsky.feed.getTimeline()
200
+ const res1 = await createPost(agent)
201
201
  AtpAgent.configure({ fetch: defaultFetchHandler })
202
202
 
203
203
  expect(res1.success).toEqual(true)
@@ -267,9 +267,9 @@ describe('agent', () => {
267
267
  // put the agent through the auth flow
268
268
  AtpAgent.configure({ fetch: tokenExpiredFetchHandler })
269
269
  const [res1, res2, res3] = await Promise.all([
270
- agent.api.app.bsky.feed.getTimeline(),
271
- agent.api.app.bsky.feed.getTimeline(),
272
- agent.api.app.bsky.feed.getTimeline(),
270
+ createPost(agent),
271
+ createPost(agent),
272
+ createPost(agent),
273
273
  ])
274
274
  AtpAgent.configure({ fetch: defaultFetchHandler })
275
275
 
@@ -462,3 +462,14 @@ describe('agent', () => {
462
462
  })
463
463
  })
464
464
  })
465
+
466
+ const createPost = async (agent: AtpAgent) => {
467
+ return agent.api.com.atproto.repo.createRecord({
468
+ repo: agent.session?.did ?? '',
469
+ collection: 'app.bsky.feed.post',
470
+ record: {
471
+ text: 'hello there',
472
+ createdAt: new Date().toISOString(),
473
+ },
474
+ })
475
+ }
@@ -20,6 +20,20 @@ describe('agent', () => {
20
20
  await close()
21
21
  })
22
22
 
23
+ const getProfileDisplayName = async (
24
+ agent: BskyAgent,
25
+ ): Promise<string | undefined> => {
26
+ try {
27
+ const res = await agent.api.app.bsky.actor.profile.get({
28
+ repo: agent.session?.did || '',
29
+ rkey: 'self',
30
+ })
31
+ return res.value.displayName ?? ''
32
+ } catch (err) {
33
+ return undefined
34
+ }
35
+ }
36
+
23
37
  it('upsertProfile correctly creates and updates profiles.', async () => {
24
38
  const agent = new BskyAgent({ service: server.url })
25
39
 
@@ -28,9 +42,8 @@ describe('agent', () => {
28
42
  email: 'user1@test.com',
29
43
  password: 'password',
30
44
  })
31
-
32
- const profile1 = await agent.getProfile({ actor: agent.session?.did || '' })
33
- expect(profile1.data.displayName).toBeFalsy()
45
+ const displayName1 = await getProfileDisplayName(agent)
46
+ expect(displayName1).toBeFalsy()
34
47
 
35
48
  await agent.upsertProfile((existing) => {
36
49
  expect(existing).toBeFalsy()
@@ -39,8 +52,8 @@ describe('agent', () => {
39
52
  }
40
53
  })
41
54
 
42
- const profile2 = await agent.getProfile({ actor: agent.session?.did || '' })
43
- expect(profile2.data.displayName).toBe('Bob')
55
+ const displayName2 = await getProfileDisplayName(agent)
56
+ expect(displayName2).toBe('Bob')
44
57
 
45
58
  await agent.upsertProfile((existing) => {
46
59
  expect(existing).toBeTruthy()
@@ -49,8 +62,8 @@ describe('agent', () => {
49
62
  }
50
63
  })
51
64
 
52
- const profile3 = await agent.getProfile({ actor: agent.session?.did || '' })
53
- expect(profile3.data.displayName).toBe('BOB')
65
+ const displayName3 = await getProfileDisplayName(agent)
66
+ expect(displayName3).toBe('BOB')
54
67
  })
55
68
 
56
69
  it('upsertProfile correctly handles CAS failures.', async () => {
@@ -62,12 +75,12 @@ describe('agent', () => {
62
75
  password: 'password',
63
76
  })
64
77
 
65
- const profile1 = await agent.getProfile({ actor: agent.session?.did || '' })
66
- expect(profile1.data.displayName).toBeFalsy()
78
+ const displayName1 = await getProfileDisplayName(agent)
79
+ expect(displayName1).toBeFalsy()
67
80
 
68
81
  let hasConflicted = false
69
82
  let ranTwice = false
70
- await agent.upsertProfile(async (existing) => {
83
+ await agent.upsertProfile(async (_existing) => {
71
84
  if (!hasConflicted) {
72
85
  await agent.com.atproto.repo.putRecord({
73
86
  repo: agent.session?.did || '',
@@ -88,8 +101,8 @@ describe('agent', () => {
88
101
  })
89
102
  expect(ranTwice).toBe(true)
90
103
 
91
- const profile2 = await agent.getProfile({ actor: agent.session?.did || '' })
92
- expect(profile2.data.displayName).toBe('Bob')
104
+ const displayName2 = await getProfileDisplayName(agent)
105
+ expect(displayName2).toBe('Bob')
93
106
  })
94
107
 
95
108
  it('upsertProfile wont endlessly retry CAS failures.', async () => {
@@ -101,10 +114,10 @@ describe('agent', () => {
101
114
  password: 'password',
102
115
  })
103
116
 
104
- const profile1 = await agent.getProfile({ actor: agent.session?.did || '' })
105
- expect(profile1.data.displayName).toBeFalsy()
117
+ const displayName1 = await getProfileDisplayName(agent)
118
+ expect(displayName1).toBeFalsy()
106
119
 
107
- const p = agent.upsertProfile(async (existing) => {
120
+ const p = agent.upsertProfile(async (_existing) => {
108
121
  await agent.com.atproto.repo.putRecord({
109
122
  repo: agent.session?.did || '',
110
123
  collection: 'app.bsky.actor.profile',
@@ -130,7 +143,7 @@ describe('agent', () => {
130
143
  password: 'password',
131
144
  })
132
145
 
133
- const p = agent.upsertProfile((existing) => {
146
+ const p = agent.upsertProfile((_existing) => {
134
147
  return {
135
148
  displayName: { string: 'Bob' },
136
149
  } as unknown as AppBskyActorProfile.Record