@ewanc26/atproto 0.2.9 → 0.2.11
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/agents.d.ts.map +1 -1
- package/dist/agents.js +17 -3
- package/dist/fetch.d.ts +7 -1
- package/dist/fetch.d.ts.map +1 -1
- package/dist/fetch.js +231 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/jetstream.d.ts +100 -0
- package/dist/jetstream.d.ts.map +1 -0
- package/dist/jetstream.js +270 -0
- package/dist/musicbrainz.d.ts +1 -0
- package/dist/musicbrainz.d.ts.map +1 -1
- package/dist/musicbrainz.js +15 -2
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/agents.ts +30 -4
- package/src/fetch.ts +296 -1
- package/src/index.ts +15 -2
- package/src/musicbrainz.ts +20 -2
- package/src/types.ts +72 -0
package/src/fetch.ts
CHANGED
|
@@ -18,7 +18,13 @@ import type {
|
|
|
18
18
|
SifaProject,
|
|
19
19
|
SifaLanguage,
|
|
20
20
|
SifaCertification,
|
|
21
|
-
SifaExternalAccount
|
|
21
|
+
SifaExternalAccount,
|
|
22
|
+
SifaPosition,
|
|
23
|
+
SifaEducation,
|
|
24
|
+
SifaVolunteering,
|
|
25
|
+
SifaHonor,
|
|
26
|
+
SifaCourse,
|
|
27
|
+
SifaPublication
|
|
22
28
|
} from './types.js';
|
|
23
29
|
|
|
24
30
|
export async function fetchProfile(did: string, fetchFn?: typeof fetch): Promise<ProfileData> {
|
|
@@ -369,6 +375,295 @@ export async function fetchRecentPopfeedReviews(
|
|
|
369
375
|
}
|
|
370
376
|
}
|
|
371
377
|
|
|
378
|
+
export async function fetchSifaPositions(
|
|
379
|
+
did: string,
|
|
380
|
+
fetchFn?: typeof fetch
|
|
381
|
+
): Promise<SifaPosition[]> {
|
|
382
|
+
const cacheKey = `sifa:positions:${did}`;
|
|
383
|
+
const cached = cache.get<SifaPosition[]>(cacheKey);
|
|
384
|
+
if (cached) return cached;
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
const records = await withFallback(
|
|
388
|
+
did,
|
|
389
|
+
async (agent) => {
|
|
390
|
+
const response = await agent.com.atproto.repo.listRecords({
|
|
391
|
+
repo: did,
|
|
392
|
+
collection: 'id.sifa.profile.position',
|
|
393
|
+
limit: 100
|
|
394
|
+
});
|
|
395
|
+
return response.data.records;
|
|
396
|
+
},
|
|
397
|
+
true,
|
|
398
|
+
fetchFn
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
const data: SifaPosition[] = records.map((record) => {
|
|
402
|
+
const value = record.value as any;
|
|
403
|
+
return {
|
|
404
|
+
company: value.company,
|
|
405
|
+
companyDid: value.companyDid,
|
|
406
|
+
title: value.title,
|
|
407
|
+
description: value.description,
|
|
408
|
+
employmentType: value.employmentType,
|
|
409
|
+
workplaceType: value.workplaceType,
|
|
410
|
+
location: value.location,
|
|
411
|
+
startedAt: value.startedAt,
|
|
412
|
+
endedAt: value.endedAt,
|
|
413
|
+
skills: value.skills,
|
|
414
|
+
isPrimary: value.isPrimary,
|
|
415
|
+
uri: record.uri
|
|
416
|
+
};
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
data.sort((a, b) => {
|
|
420
|
+
if (!a.startedAt) return 1;
|
|
421
|
+
if (!b.startedAt) return -1;
|
|
422
|
+
return new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime();
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
cache.set(cacheKey, data);
|
|
426
|
+
return data;
|
|
427
|
+
} catch {
|
|
428
|
+
return [];
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export async function fetchSifaEducation(
|
|
433
|
+
did: string,
|
|
434
|
+
fetchFn?: typeof fetch
|
|
435
|
+
): Promise<SifaEducation[]> {
|
|
436
|
+
const cacheKey = `sifa:education:${did}`;
|
|
437
|
+
const cached = cache.get<SifaEducation[]>(cacheKey);
|
|
438
|
+
if (cached) return cached;
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
const records = await withFallback(
|
|
442
|
+
did,
|
|
443
|
+
async (agent) => {
|
|
444
|
+
const response = await agent.com.atproto.repo.listRecords({
|
|
445
|
+
repo: did,
|
|
446
|
+
collection: 'id.sifa.profile.education',
|
|
447
|
+
limit: 100
|
|
448
|
+
});
|
|
449
|
+
return response.data.records;
|
|
450
|
+
},
|
|
451
|
+
true,
|
|
452
|
+
fetchFn
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
const data: SifaEducation[] = records.map((record) => {
|
|
456
|
+
const value = record.value as any;
|
|
457
|
+
return {
|
|
458
|
+
institution: value.institution,
|
|
459
|
+
institutionDid: value.institutionDid,
|
|
460
|
+
degree: value.degree,
|
|
461
|
+
fieldOfStudy: value.fieldOfStudy,
|
|
462
|
+
grade: value.grade,
|
|
463
|
+
activities: value.activities,
|
|
464
|
+
description: value.description,
|
|
465
|
+
location: value.location,
|
|
466
|
+
startedAt: value.startedAt,
|
|
467
|
+
endedAt: value.endedAt,
|
|
468
|
+
uri: record.uri
|
|
469
|
+
};
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
data.sort((a, b) => {
|
|
473
|
+
const aEnd = a.endedAt ? new Date(a.endedAt).getTime() : Date.now();
|
|
474
|
+
const bEnd = b.endedAt ? new Date(b.endedAt).getTime() : Date.now();
|
|
475
|
+
return bEnd - aEnd;
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
cache.set(cacheKey, data);
|
|
479
|
+
return data;
|
|
480
|
+
} catch {
|
|
481
|
+
return [];
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export async function fetchSifaVolunteering(
|
|
486
|
+
did: string,
|
|
487
|
+
fetchFn?: typeof fetch
|
|
488
|
+
): Promise<SifaVolunteering[]> {
|
|
489
|
+
const cacheKey = `sifa:volunteering:${did}`;
|
|
490
|
+
const cached = cache.get<SifaVolunteering[]>(cacheKey);
|
|
491
|
+
if (cached) return cached;
|
|
492
|
+
|
|
493
|
+
try {
|
|
494
|
+
const records = await withFallback(
|
|
495
|
+
did,
|
|
496
|
+
async (agent) => {
|
|
497
|
+
const response = await agent.com.atproto.repo.listRecords({
|
|
498
|
+
repo: did,
|
|
499
|
+
collection: 'id.sifa.profile.volunteering',
|
|
500
|
+
limit: 100
|
|
501
|
+
});
|
|
502
|
+
return response.data.records;
|
|
503
|
+
},
|
|
504
|
+
true,
|
|
505
|
+
fetchFn
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
const data: SifaVolunteering[] = records.map((record) => {
|
|
509
|
+
const value = record.value as any;
|
|
510
|
+
return {
|
|
511
|
+
organization: value.organization,
|
|
512
|
+
organizationDid: value.organizationDid,
|
|
513
|
+
role: value.role,
|
|
514
|
+
cause: value.cause,
|
|
515
|
+
description: value.description,
|
|
516
|
+
startedAt: value.startedAt,
|
|
517
|
+
endedAt: value.endedAt,
|
|
518
|
+
uri: record.uri
|
|
519
|
+
};
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
cache.set(cacheKey, data);
|
|
523
|
+
return data;
|
|
524
|
+
} catch {
|
|
525
|
+
return [];
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
export async function fetchSifaHonors(
|
|
530
|
+
did: string,
|
|
531
|
+
fetchFn?: typeof fetch
|
|
532
|
+
): Promise<SifaHonor[]> {
|
|
533
|
+
const cacheKey = `sifa:honors:${did}`;
|
|
534
|
+
const cached = cache.get<SifaHonor[]>(cacheKey);
|
|
535
|
+
if (cached) return cached;
|
|
536
|
+
|
|
537
|
+
try {
|
|
538
|
+
const records = await withFallback(
|
|
539
|
+
did,
|
|
540
|
+
async (agent) => {
|
|
541
|
+
const response = await agent.com.atproto.repo.listRecords({
|
|
542
|
+
repo: did,
|
|
543
|
+
collection: 'id.sifa.profile.honor',
|
|
544
|
+
limit: 100
|
|
545
|
+
});
|
|
546
|
+
return response.data.records;
|
|
547
|
+
},
|
|
548
|
+
true,
|
|
549
|
+
fetchFn
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
const data: SifaHonor[] = records.map((record) => {
|
|
553
|
+
const value = record.value as any;
|
|
554
|
+
return {
|
|
555
|
+
title: value.title,
|
|
556
|
+
issuer: value.issuer,
|
|
557
|
+
issuerDid: value.issuerDid,
|
|
558
|
+
description: value.description,
|
|
559
|
+
awardedAt: value.awardedAt,
|
|
560
|
+
uri: record.uri
|
|
561
|
+
};
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
data.sort((a, b) => {
|
|
565
|
+
if (!a.awardedAt) return 1;
|
|
566
|
+
if (!b.awardedAt) return -1;
|
|
567
|
+
return new Date(b.awardedAt).getTime() - new Date(a.awardedAt).getTime();
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
cache.set(cacheKey, data);
|
|
571
|
+
return data;
|
|
572
|
+
} catch {
|
|
573
|
+
return [];
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
export async function fetchSifaCourses(
|
|
578
|
+
did: string,
|
|
579
|
+
fetchFn?: typeof fetch
|
|
580
|
+
): Promise<SifaCourse[]> {
|
|
581
|
+
const cacheKey = `sifa:courses:${did}`;
|
|
582
|
+
const cached = cache.get<SifaCourse[]>(cacheKey);
|
|
583
|
+
if (cached) return cached;
|
|
584
|
+
|
|
585
|
+
try {
|
|
586
|
+
const records = await withFallback(
|
|
587
|
+
did,
|
|
588
|
+
async (agent) => {
|
|
589
|
+
const response = await agent.com.atproto.repo.listRecords({
|
|
590
|
+
repo: did,
|
|
591
|
+
collection: 'id.sifa.profile.course',
|
|
592
|
+
limit: 100
|
|
593
|
+
});
|
|
594
|
+
return response.data.records;
|
|
595
|
+
},
|
|
596
|
+
true,
|
|
597
|
+
fetchFn
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
const data: SifaCourse[] = records.map((record) => {
|
|
601
|
+
const value = record.value as any;
|
|
602
|
+
return {
|
|
603
|
+
name: value.name,
|
|
604
|
+
number: value.number,
|
|
605
|
+
institution: value.institution,
|
|
606
|
+
education: value.education,
|
|
607
|
+
uri: record.uri
|
|
608
|
+
};
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
cache.set(cacheKey, data);
|
|
612
|
+
return data;
|
|
613
|
+
} catch {
|
|
614
|
+
return [];
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
export async function fetchSifaPublications(
|
|
619
|
+
did: string,
|
|
620
|
+
fetchFn?: typeof fetch
|
|
621
|
+
): Promise<SifaPublication[]> {
|
|
622
|
+
const cacheKey = `sifa:publications:${did}`;
|
|
623
|
+
const cached = cache.get<SifaPublication[]>(cacheKey);
|
|
624
|
+
if (cached) return cached;
|
|
625
|
+
|
|
626
|
+
try {
|
|
627
|
+
const records = await withFallback(
|
|
628
|
+
did,
|
|
629
|
+
async (agent) => {
|
|
630
|
+
const response = await agent.com.atproto.repo.listRecords({
|
|
631
|
+
repo: did,
|
|
632
|
+
collection: 'id.sifa.profile.publication',
|
|
633
|
+
limit: 100
|
|
634
|
+
});
|
|
635
|
+
return response.data.records;
|
|
636
|
+
},
|
|
637
|
+
true,
|
|
638
|
+
fetchFn
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
const data: SifaPublication[] = records.map((record) => {
|
|
642
|
+
const value = record.value as any;
|
|
643
|
+
return {
|
|
644
|
+
title: value.title,
|
|
645
|
+
publisher: value.publisher,
|
|
646
|
+
url: value.url,
|
|
647
|
+
description: value.description,
|
|
648
|
+
authors: value.authors,
|
|
649
|
+
publishedAt: value.publishedAt,
|
|
650
|
+
uri: record.uri
|
|
651
|
+
};
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
data.sort((a, b) => {
|
|
655
|
+
if (!a.publishedAt) return 1;
|
|
656
|
+
if (!b.publishedAt) return -1;
|
|
657
|
+
return new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime();
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
cache.set(cacheKey, data);
|
|
661
|
+
return data;
|
|
662
|
+
} catch {
|
|
663
|
+
return [];
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
372
667
|
export async function fetchTangledRepos(
|
|
373
668
|
did: string,
|
|
374
669
|
fetchFn?: typeof fetch
|
package/src/index.ts
CHANGED
|
@@ -44,7 +44,14 @@ export type {
|
|
|
44
44
|
SifaLanguage,
|
|
45
45
|
SifaCertification,
|
|
46
46
|
SifaExternalAccount,
|
|
47
|
-
SifaLocation
|
|
47
|
+
SifaLocation,
|
|
48
|
+
SifaPosition,
|
|
49
|
+
SifaEducation,
|
|
50
|
+
SifaVolunteering,
|
|
51
|
+
SifaHonor,
|
|
52
|
+
SifaCourse,
|
|
53
|
+
SifaPublication,
|
|
54
|
+
SifaPublicationAuthor
|
|
48
55
|
} from './types.js';
|
|
49
56
|
|
|
50
57
|
export {
|
|
@@ -60,7 +67,13 @@ export {
|
|
|
60
67
|
fetchSifaProjects,
|
|
61
68
|
fetchSifaLanguages,
|
|
62
69
|
fetchSifaCertifications,
|
|
63
|
-
fetchSifaExternalAccounts
|
|
70
|
+
fetchSifaExternalAccounts,
|
|
71
|
+
fetchSifaPositions,
|
|
72
|
+
fetchSifaEducation,
|
|
73
|
+
fetchSifaVolunteering,
|
|
74
|
+
fetchSifaHonors,
|
|
75
|
+
fetchSifaCourses,
|
|
76
|
+
fetchSifaPublications
|
|
64
77
|
} from './fetch.js';
|
|
65
78
|
|
|
66
79
|
export {
|
package/src/musicbrainz.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { cache } from './cache.js';
|
|
2
2
|
|
|
3
|
+
/** Timeout for individual artwork API calls (ms). */
|
|
4
|
+
const ARTWORK_TIMEOUT = 5_000;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wraps a promise with a timeout. Rejects with a TimeoutError if the promise
|
|
8
|
+
* doesn't settle within `ms` milliseconds.
|
|
9
|
+
*/
|
|
10
|
+
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
|
11
|
+
return new Promise<T>((resolve, reject) => {
|
|
12
|
+
const timer = setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms);
|
|
13
|
+
promise.then(
|
|
14
|
+
(value) => { clearTimeout(timer); resolve(value); },
|
|
15
|
+
(err) => { clearTimeout(timer); reject(err); }
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
3
20
|
interface MusicBrainzRelease {
|
|
4
21
|
id: string;
|
|
5
22
|
score: number;
|
|
@@ -147,6 +164,7 @@ export async function searchLastFmArtwork(
|
|
|
147
164
|
|
|
148
165
|
/**
|
|
149
166
|
* Cascading artwork search: Cover Art Archive → MusicBrainz+CAA → iTunes → Last.fm
|
|
167
|
+
* Each step is bounded by ARTWORK_TIMEOUT to prevent serverless function timeouts.
|
|
150
168
|
*/
|
|
151
169
|
export async function findArtwork(
|
|
152
170
|
trackName: string,
|
|
@@ -161,7 +179,7 @@ export async function findArtwork(
|
|
|
161
179
|
if (releaseMbId) {
|
|
162
180
|
const caaUrl = buildCoverArtUrl(releaseMbId, 500);
|
|
163
181
|
try {
|
|
164
|
-
const res = await _fetch(caaUrl, { method: 'HEAD' });
|
|
182
|
+
const res = await withTimeout(_fetch(caaUrl, { method: 'HEAD' }), ARTWORK_TIMEOUT);
|
|
165
183
|
if (res.ok) return caaUrl;
|
|
166
184
|
} catch { /* continue */ }
|
|
167
185
|
}
|
|
@@ -171,7 +189,7 @@ export async function findArtwork(
|
|
|
171
189
|
if (mbId) {
|
|
172
190
|
const caaUrl = buildCoverArtUrl(mbId, 500);
|
|
173
191
|
try {
|
|
174
|
-
const res = await _fetch(caaUrl, { method: 'HEAD' });
|
|
192
|
+
const res = await withTimeout(_fetch(caaUrl, { method: 'HEAD' }), ARTWORK_TIMEOUT);
|
|
175
193
|
if (res.ok) return caaUrl;
|
|
176
194
|
} catch { /* continue */ }
|
|
177
195
|
}
|
package/src/types.ts
CHANGED
|
@@ -377,3 +377,75 @@ export interface SifaExternalAccount {
|
|
|
377
377
|
isPrimary?: boolean;
|
|
378
378
|
uri: string;
|
|
379
379
|
}
|
|
380
|
+
|
|
381
|
+
export interface SifaPosition {
|
|
382
|
+
company: string;
|
|
383
|
+
companyDid?: string;
|
|
384
|
+
title: string;
|
|
385
|
+
description?: string;
|
|
386
|
+
employmentType?: string;
|
|
387
|
+
workplaceType?: string;
|
|
388
|
+
location?: SifaLocation;
|
|
389
|
+
startedAt: string;
|
|
390
|
+
endedAt?: string;
|
|
391
|
+
skills?: string[];
|
|
392
|
+
isPrimary?: boolean;
|
|
393
|
+
uri: string;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export interface SifaEducation {
|
|
397
|
+
institution: string;
|
|
398
|
+
institutionDid?: string;
|
|
399
|
+
degree?: string;
|
|
400
|
+
fieldOfStudy?: string;
|
|
401
|
+
grade?: string;
|
|
402
|
+
activities?: string;
|
|
403
|
+
description?: string;
|
|
404
|
+
location?: SifaLocation;
|
|
405
|
+
startedAt?: string;
|
|
406
|
+
endedAt?: string;
|
|
407
|
+
uri: string;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export interface SifaVolunteering {
|
|
411
|
+
organization: string;
|
|
412
|
+
organizationDid?: string;
|
|
413
|
+
role?: string;
|
|
414
|
+
cause?: string;
|
|
415
|
+
description?: string;
|
|
416
|
+
startedAt?: string;
|
|
417
|
+
endedAt?: string;
|
|
418
|
+
uri: string;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export interface SifaHonor {
|
|
422
|
+
title: string;
|
|
423
|
+
issuer?: string;
|
|
424
|
+
issuerDid?: string;
|
|
425
|
+
description?: string;
|
|
426
|
+
awardedAt?: string;
|
|
427
|
+
uri: string;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export interface SifaCourse {
|
|
431
|
+
name: string;
|
|
432
|
+
number?: string;
|
|
433
|
+
institution?: string;
|
|
434
|
+
education?: string;
|
|
435
|
+
uri: string;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export interface SifaPublicationAuthor {
|
|
439
|
+
name: string;
|
|
440
|
+
did?: string;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export interface SifaPublication {
|
|
444
|
+
title: string;
|
|
445
|
+
publisher?: string;
|
|
446
|
+
url?: string;
|
|
447
|
+
description?: string;
|
|
448
|
+
authors?: SifaPublicationAuthor[];
|
|
449
|
+
publishedAt?: string;
|
|
450
|
+
uri: string;
|
|
451
|
+
}
|