@atproto/api 0.6.11 → 0.6.12

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/dist/types.d.ts CHANGED
@@ -40,5 +40,6 @@ export interface BskyPreferences {
40
40
  };
41
41
  adultContentEnabled: boolean;
42
42
  contentLabels: Record<string, BskyLabelPreference>;
43
+ birthDate: Date | undefined;
43
44
  }
44
45
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/api",
3
- "version": "0.6.11",
3
+ "version": "0.6.12",
4
4
  "main": "dist/index.js",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -13,14 +13,14 @@
13
13
  "tlds": "^1.234.0",
14
14
  "typed-emitter": "^2.1.0",
15
15
  "@atproto/common-web": "^0.2.0",
16
- "@atproto/lexicon": "^0.2.0",
17
16
  "@atproto/syntax": "^0.1.0",
17
+ "@atproto/lexicon": "^0.2.0",
18
18
  "@atproto/xrpc": "^0.3.0"
19
19
  },
20
20
  "devDependencies": {
21
21
  "common-tags": "^1.8.2",
22
- "@atproto/lex-cli": "^0.2.0",
23
- "@atproto/pds": "^0.1.12"
22
+ "@atproto/pds": "^0.1.12",
23
+ "@atproto/lex-cli": "^0.2.0"
24
24
  },
25
25
  "scripts": {
26
26
  "codegen": "pnpm docgen && node ./scripts/generate-code.mjs && lex gen-api ./src/client ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/*",
package/src/bsky-agent.ts CHANGED
@@ -8,6 +8,15 @@ import {
8
8
  } from './client'
9
9
  import { BskyPreferences, BskyLabelPreference } from './types'
10
10
 
11
+ declare global {
12
+ interface Array<T> {
13
+ findLast(
14
+ predicate: (value: T, index: number, obj: T[]) => unknown,
15
+ thisArg?: any,
16
+ ): T
17
+ }
18
+ }
19
+
11
20
  export class BskyAgent extends AtpAgent {
12
21
  get app() {
13
22
  return this.api.app
@@ -247,6 +256,7 @@ export class BskyAgent extends AtpAgent {
247
256
  },
248
257
  adultContentEnabled: false,
249
258
  contentLabels: {},
259
+ birthDate: undefined,
250
260
  }
251
261
  const res = await this.app.bsky.actor.getPreferences({})
252
262
  for (const pref of res.data.preferences) {
@@ -272,6 +282,13 @@ export class BskyAgent extends AtpAgent {
272
282
  ) {
273
283
  prefs.feeds.saved = pref.saved
274
284
  prefs.feeds.pinned = pref.pinned
285
+ } else if (
286
+ AppBskyActorDefs.isPersonalDetailsPref(pref) &&
287
+ AppBskyActorDefs.validatePersonalDetailsPref(pref).success
288
+ ) {
289
+ if (pref.birthDate) {
290
+ prefs.birthDate = new Date(pref.birthDate)
291
+ }
275
292
  }
276
293
  }
277
294
  return prefs
@@ -314,20 +331,22 @@ export class BskyAgent extends AtpAgent {
314
331
 
315
332
  async setAdultContentEnabled(v: boolean) {
316
333
  await updatePreferences(this, (prefs: AppBskyActorDefs.Preferences) => {
317
- const existing = prefs.find(
334
+ let adultContentPref = prefs.findLast(
318
335
  (pref) =>
319
336
  AppBskyActorDefs.isAdultContentPref(pref) &&
320
337
  AppBskyActorDefs.validateAdultContentPref(pref).success,
321
338
  )
322
- if (existing) {
323
- existing.enabled = v
339
+ if (adultContentPref) {
340
+ adultContentPref.enabled = v
324
341
  } else {
325
- prefs.push({
342
+ adultContentPref = {
326
343
  $type: 'app.bsky.actor.defs#adultContentPref',
327
344
  enabled: v,
328
- })
345
+ }
329
346
  }
330
347
  return prefs
348
+ .filter((pref) => !AppBskyActorDefs.isAdultContentPref(pref))
349
+ .concat([adultContentPref])
331
350
  })
332
351
  }
333
352
 
@@ -338,22 +357,53 @@ export class BskyAgent extends AtpAgent {
338
357
  }
339
358
 
340
359
  await updatePreferences(this, (prefs: AppBskyActorDefs.Preferences) => {
341
- const existing = prefs.find(
360
+ let labelPref = prefs.findLast(
342
361
  (pref) =>
343
362
  AppBskyActorDefs.isContentLabelPref(pref) &&
344
363
  AppBskyActorDefs.validateAdultContentPref(pref).success &&
345
364
  pref.label === key,
346
365
  )
347
- if (existing) {
348
- existing.visibility = value
366
+ if (labelPref) {
367
+ labelPref.visibility = value
349
368
  } else {
350
- prefs.push({
369
+ labelPref = {
351
370
  $type: 'app.bsky.actor.defs#contentLabelPref',
352
371
  label: key,
353
372
  visibility: value,
354
- })
373
+ }
374
+ }
375
+ return prefs
376
+ .filter(
377
+ (pref) =>
378
+ !AppBskyActorDefs.isContentLabelPref(pref) || pref.label !== key,
379
+ )
380
+ .concat([labelPref])
381
+ })
382
+ }
383
+
384
+ async setPersonalDetails({
385
+ birthDate,
386
+ }: {
387
+ birthDate: string | Date | undefined
388
+ }) {
389
+ birthDate = birthDate instanceof Date ? birthDate.toISOString() : birthDate
390
+ await updatePreferences(this, (prefs: AppBskyActorDefs.Preferences) => {
391
+ let personalDetailsPref = prefs.findLast(
392
+ (pref) =>
393
+ AppBskyActorDefs.isPersonalDetailsPref(pref) &&
394
+ AppBskyActorDefs.validatePersonalDetailsPref(pref).success,
395
+ )
396
+ if (personalDetailsPref) {
397
+ personalDetailsPref.birthDate = birthDate
398
+ } else {
399
+ personalDetailsPref = {
400
+ $type: 'app.bsky.actor.defs#personalDetailsPref',
401
+ birthDate,
402
+ }
355
403
  }
356
404
  return prefs
405
+ .filter((pref) => !AppBskyActorDefs.isPersonalDetailsPref(pref))
406
+ .concat([personalDetailsPref])
357
407
  })
358
408
  }
359
409
  }
@@ -394,7 +444,7 @@ async function updateFeedPreferences(
394
444
  ): Promise<{ saved: string[]; pinned: string[] }> {
395
445
  let res
396
446
  await updatePreferences(agent, (prefs: AppBskyActorDefs.Preferences) => {
397
- let feedsPref = prefs.find(
447
+ let feedsPref = prefs.findLast(
398
448
  (pref) =>
399
449
  AppBskyActorDefs.isSavedFeedsPref(pref) &&
400
450
  AppBskyActorDefs.validateSavedFeedsPref(pref).success,
package/src/types.ts CHANGED
@@ -89,4 +89,5 @@ export interface BskyPreferences {
89
89
  }
90
90
  adultContentEnabled: boolean
91
91
  contentLabels: Record<string, BskyLabelPreference>
92
+ birthDate: Date | undefined
92
93
  }
@@ -215,6 +215,7 @@ describe('agent', () => {
215
215
  feeds: { pinned: undefined, saved: undefined },
216
216
  adultContentEnabled: false,
217
217
  contentLabels: {},
218
+ birthDate: undefined,
218
219
  })
219
220
 
220
221
  await agent.setAdultContentEnabled(true)
@@ -222,6 +223,7 @@ describe('agent', () => {
222
223
  feeds: { pinned: undefined, saved: undefined },
223
224
  adultContentEnabled: true,
224
225
  contentLabels: {},
226
+ birthDate: undefined,
225
227
  })
226
228
 
227
229
  await agent.setAdultContentEnabled(false)
@@ -229,6 +231,7 @@ describe('agent', () => {
229
231
  feeds: { pinned: undefined, saved: undefined },
230
232
  adultContentEnabled: false,
231
233
  contentLabels: {},
234
+ birthDate: undefined,
232
235
  })
233
236
 
234
237
  await agent.setContentLabelPref('impersonation', 'warn')
@@ -238,6 +241,7 @@ describe('agent', () => {
238
241
  contentLabels: {
239
242
  impersonation: 'warn',
240
243
  },
244
+ birthDate: undefined,
241
245
  })
242
246
 
243
247
  await agent.setContentLabelPref('spam', 'show') // will convert to 'ignore'
@@ -249,6 +253,7 @@ describe('agent', () => {
249
253
  impersonation: 'hide',
250
254
  spam: 'ignore',
251
255
  },
256
+ birthDate: undefined,
252
257
  })
253
258
 
254
259
  await agent.addSavedFeed('at://bob.com/app.bsky.feed.generator/fake')
@@ -262,6 +267,7 @@ describe('agent', () => {
262
267
  impersonation: 'hide',
263
268
  spam: 'ignore',
264
269
  },
270
+ birthDate: undefined,
265
271
  })
266
272
 
267
273
  await agent.addPinnedFeed('at://bob.com/app.bsky.feed.generator/fake')
@@ -275,6 +281,7 @@ describe('agent', () => {
275
281
  impersonation: 'hide',
276
282
  spam: 'ignore',
277
283
  },
284
+ birthDate: undefined,
278
285
  })
279
286
 
280
287
  await agent.removePinnedFeed('at://bob.com/app.bsky.feed.generator/fake')
@@ -288,6 +295,7 @@ describe('agent', () => {
288
295
  impersonation: 'hide',
289
296
  spam: 'ignore',
290
297
  },
298
+ birthDate: undefined,
291
299
  })
292
300
 
293
301
  await agent.removeSavedFeed('at://bob.com/app.bsky.feed.generator/fake')
@@ -301,6 +309,7 @@ describe('agent', () => {
301
309
  impersonation: 'hide',
302
310
  spam: 'ignore',
303
311
  },
312
+ birthDate: undefined,
304
313
  })
305
314
 
306
315
  await agent.addPinnedFeed('at://bob.com/app.bsky.feed.generator/fake')
@@ -314,6 +323,7 @@ describe('agent', () => {
314
323
  impersonation: 'hide',
315
324
  spam: 'ignore',
316
325
  },
326
+ birthDate: undefined,
317
327
  })
318
328
 
319
329
  await agent.addPinnedFeed('at://bob.com/app.bsky.feed.generator/fake2')
@@ -333,6 +343,7 @@ describe('agent', () => {
333
343
  impersonation: 'hide',
334
344
  spam: 'ignore',
335
345
  },
346
+ birthDate: undefined,
336
347
  })
337
348
 
338
349
  await agent.removeSavedFeed('at://bob.com/app.bsky.feed.generator/fake')
@@ -346,7 +357,178 @@ describe('agent', () => {
346
357
  impersonation: 'hide',
347
358
  spam: 'ignore',
348
359
  },
360
+ birthDate: undefined,
349
361
  })
362
+
363
+ await agent.setPersonalDetails({ birthDate: '2023-09-11T18:05:42.556Z' })
364
+ await expect(agent.getPreferences()).resolves.toStrictEqual({
365
+ feeds: {
366
+ pinned: ['at://bob.com/app.bsky.feed.generator/fake2'],
367
+ saved: ['at://bob.com/app.bsky.feed.generator/fake2'],
368
+ },
369
+ adultContentEnabled: false,
370
+ contentLabels: {
371
+ impersonation: 'hide',
372
+ spam: 'ignore',
373
+ },
374
+ birthDate: new Date('2023-09-11T18:05:42.556Z'),
375
+ })
376
+ })
377
+
378
+ it('resolves duplicates correctly', async () => {
379
+ const agent = new BskyAgent({ service: server.url })
380
+
381
+ await agent.createAccount({
382
+ handle: 'user6.test',
383
+ email: 'user6@test.com',
384
+ password: 'password',
385
+ })
386
+
387
+ await agent.app.bsky.actor.putPreferences({
388
+ preferences: [
389
+ {
390
+ $type: 'app.bsky.actor.defs#contentLabelPref',
391
+ label: 'nsfw',
392
+ visibility: 'show',
393
+ },
394
+ {
395
+ $type: 'app.bsky.actor.defs#contentLabelPref',
396
+ label: 'nsfw',
397
+ visibility: 'hide',
398
+ },
399
+ {
400
+ $type: 'app.bsky.actor.defs#contentLabelPref',
401
+ label: 'nsfw',
402
+ visibility: 'show',
403
+ },
404
+ {
405
+ $type: 'app.bsky.actor.defs#contentLabelPref',
406
+ label: 'nsfw',
407
+ visibility: 'warn',
408
+ },
409
+ {
410
+ $type: 'app.bsky.actor.defs#adultContentPref',
411
+ enabled: true,
412
+ },
413
+ {
414
+ $type: 'app.bsky.actor.defs#adultContentPref',
415
+ enabled: false,
416
+ },
417
+ {
418
+ $type: 'app.bsky.actor.defs#adultContentPref',
419
+ enabled: true,
420
+ },
421
+ {
422
+ $type: 'app.bsky.actor.defs#savedFeedsPref',
423
+ pinned: [
424
+ 'at://bob.com/app.bsky.feed.generator/fake',
425
+ 'at://bob.com/app.bsky.feed.generator/fake2',
426
+ ],
427
+ saved: [
428
+ 'at://bob.com/app.bsky.feed.generator/fake',
429
+ 'at://bob.com/app.bsky.feed.generator/fake2',
430
+ ],
431
+ },
432
+ {
433
+ $type: 'app.bsky.actor.defs#savedFeedsPref',
434
+ pinned: [],
435
+ saved: [],
436
+ },
437
+ {
438
+ $type: 'app.bsky.actor.defs#personalDetailsPref',
439
+ birthDate: '2023-09-11T18:05:42.556Z',
440
+ },
441
+ {
442
+ $type: 'app.bsky.actor.defs#personalDetailsPref',
443
+ birthDate: '2021-09-11T18:05:42.556Z',
444
+ },
445
+ ],
446
+ })
447
+ await expect(agent.getPreferences()).resolves.toStrictEqual({
448
+ feeds: {
449
+ pinned: [],
450
+ saved: [],
451
+ },
452
+ adultContentEnabled: true,
453
+ contentLabels: {
454
+ nsfw: 'warn',
455
+ },
456
+ birthDate: new Date('2021-09-11T18:05:42.556Z'),
457
+ })
458
+
459
+ await agent.setAdultContentEnabled(false)
460
+ await expect(agent.getPreferences()).resolves.toStrictEqual({
461
+ feeds: {
462
+ pinned: [],
463
+ saved: [],
464
+ },
465
+ adultContentEnabled: false,
466
+ contentLabels: {
467
+ nsfw: 'warn',
468
+ },
469
+ birthDate: new Date('2021-09-11T18:05:42.556Z'),
470
+ })
471
+
472
+ await agent.setContentLabelPref('nsfw', 'hide')
473
+ await expect(agent.getPreferences()).resolves.toStrictEqual({
474
+ feeds: {
475
+ pinned: [],
476
+ saved: [],
477
+ },
478
+ adultContentEnabled: false,
479
+ contentLabels: {
480
+ nsfw: 'hide',
481
+ },
482
+ birthDate: new Date('2021-09-11T18:05:42.556Z'),
483
+ })
484
+
485
+ await agent.addPinnedFeed('at://bob.com/app.bsky.feed.generator/fake')
486
+ await expect(agent.getPreferences()).resolves.toStrictEqual({
487
+ feeds: {
488
+ pinned: ['at://bob.com/app.bsky.feed.generator/fake'],
489
+ saved: ['at://bob.com/app.bsky.feed.generator/fake'],
490
+ },
491
+ adultContentEnabled: false,
492
+ contentLabels: {
493
+ nsfw: 'hide',
494
+ },
495
+ birthDate: new Date('2021-09-11T18:05:42.556Z'),
496
+ })
497
+
498
+ await agent.setPersonalDetails({ birthDate: '2023-09-11T18:05:42.556Z' })
499
+ await expect(agent.getPreferences()).resolves.toStrictEqual({
500
+ feeds: {
501
+ pinned: ['at://bob.com/app.bsky.feed.generator/fake'],
502
+ saved: ['at://bob.com/app.bsky.feed.generator/fake'],
503
+ },
504
+ adultContentEnabled: false,
505
+ contentLabels: {
506
+ nsfw: 'hide',
507
+ },
508
+ birthDate: new Date('2023-09-11T18:05:42.556Z'),
509
+ })
510
+
511
+ const res = await agent.app.bsky.actor.getPreferences()
512
+ await expect(res.data.preferences).toStrictEqual([
513
+ {
514
+ $type: 'app.bsky.actor.defs#adultContentPref',
515
+ enabled: false,
516
+ },
517
+ {
518
+ $type: 'app.bsky.actor.defs#contentLabelPref',
519
+ label: 'nsfw',
520
+ visibility: 'hide',
521
+ },
522
+ {
523
+ $type: 'app.bsky.actor.defs#savedFeedsPref',
524
+ pinned: ['at://bob.com/app.bsky.feed.generator/fake'],
525
+ saved: ['at://bob.com/app.bsky.feed.generator/fake'],
526
+ },
527
+ {
528
+ $type: 'app.bsky.actor.defs#personalDetailsPref',
529
+ birthDate: '2023-09-11T18:05:42.556Z',
530
+ },
531
+ ])
350
532
  })
351
533
  })
352
534
  })