@atproto-ui/core 0.13.0

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.
Files changed (51) hide show
  1. package/dist/backlinks.d.ts +18 -0
  2. package/dist/backlinks.js +15 -0
  3. package/dist/blob-fetch.d.ts +42 -0
  4. package/dist/blob-fetch.js +53 -0
  5. package/dist/cache.d.ts +155 -0
  6. package/dist/cache.js +345 -0
  7. package/dist/client.d.ts +45 -0
  8. package/dist/client.js +118 -0
  9. package/dist/fetch-and-cache-handler.d.ts +84 -0
  10. package/dist/fetch-and-cache-handler.js +63 -0
  11. package/dist/grain.d.ts +22 -0
  12. package/dist/grain.js +30 -0
  13. package/dist/icons.d.ts +11 -0
  14. package/dist/icons.js +11 -0
  15. package/dist/identity.d.ts +28 -0
  16. package/dist/identity.js +55 -0
  17. package/dist/index.d.ts +27 -0
  18. package/dist/index.js +22 -0
  19. package/dist/leaflet.d.ts +22 -0
  20. package/dist/leaflet.js +85 -0
  21. package/dist/music.d.ts +35 -0
  22. package/dist/music.js +88 -0
  23. package/dist/paginated.d.ts +61 -0
  24. package/dist/paginated.js +111 -0
  25. package/dist/records.d.ts +171 -0
  26. package/dist/records.js +321 -0
  27. package/dist/rpc.d.ts +21 -0
  28. package/dist/rpc.js +24 -0
  29. package/dist/styles.d.ts +40 -0
  30. package/dist/styles.js +1728 -0
  31. package/dist/tangled-fetch.d.ts +9 -0
  32. package/dist/tangled-fetch.js +15 -0
  33. package/dist/types/blob.d.ts +14 -0
  34. package/dist/types/bluesky.d.ts +4 -0
  35. package/dist/types/grain.d.ts +31 -0
  36. package/dist/types/leaflet.d.ts +173 -0
  37. package/dist/types/record.d.ts +28 -0
  38. package/dist/types/record.js +7 -0
  39. package/dist/types/tangled.d.ts +19 -0
  40. package/dist/types/teal.d.ts +36 -0
  41. package/dist/utils/at-uri.d.ts +10 -0
  42. package/dist/utils/at-uri.js +33 -0
  43. package/dist/utils/blob.d.ts +5 -0
  44. package/dist/utils/blob.js +26 -0
  45. package/dist/utils/profile.d.ts +2 -0
  46. package/dist/utils/profile.js +5 -0
  47. package/dist/utils/richtext.d.ts +28 -0
  48. package/dist/utils/richtext.js +95 -0
  49. package/dist/utils/time.d.ts +2 -0
  50. package/dist/utils/time.js +41 -0
  51. package/package.json +41 -0
@@ -0,0 +1,18 @@
1
+ export interface BacklinkRecord {
2
+ did: string;
3
+ collection: string;
4
+ rkey: string;
5
+ }
6
+ export interface BacklinksResponse {
7
+ total: number;
8
+ records: BacklinkRecord[];
9
+ cursor?: string;
10
+ }
11
+ export interface FetchBacklinksOptions {
12
+ subject: string;
13
+ source: string;
14
+ limit?: number;
15
+ constellationBaseUrl?: string;
16
+ signal?: AbortSignal;
17
+ }
18
+ export declare function fetchBacklinks({ subject, source, limit, constellationBaseUrl, signal, }: FetchBacklinksOptions): Promise<BacklinksResponse>;
@@ -0,0 +1,15 @@
1
+ async function fetchBacklinks({ subject: e, source: t, limit: n = 16, constellationBaseUrl: r = "https://constellation.microcosm.blue", signal: i }) {
2
+ let a = r.endsWith("/") ? r.slice(0, -1) : r, o = new URLSearchParams({
3
+ subject: e,
4
+ source: t,
5
+ limit: String(n)
6
+ }), s = await fetch(`${a}/xrpc/blue.microcosm.links.getBacklinks?${o}`, { signal: i });
7
+ if (!s.ok) throw Error(`Failed to fetch backlinks: ${s.status} ${s.statusText}`);
8
+ let c = await s.json();
9
+ return {
10
+ total: c.total ?? 0,
11
+ records: c.records ?? [],
12
+ cursor: c.cursor
13
+ };
14
+ }
15
+ export { fetchBacklinks };
@@ -0,0 +1,42 @@
1
+ import type { BlobCache } from "./cache";
2
+ import type { ServiceResolver } from "./client";
3
+ export interface GetBlobOptions {
4
+ blobCache: BlobCache;
5
+ resolver: ServiceResolver;
6
+ handleOrDid: string;
7
+ blob: unknown;
8
+ /**
9
+ * When false, ignore appview CDN URLs and always fetch the blob from the PDS.
10
+ * Defaults to true so banned/unavailable appview accounts still resolve via PDS.
11
+ */
12
+ preferCdn?: boolean;
13
+ }
14
+ /**
15
+ * Resolves a renderable image URL for a blob.
16
+ *
17
+ * - When the blob carries an appview CDN URL, that URL is returned directly. CDN
18
+ * URLs are CORS-safe as an `<img src>` but NOT fetchable, so we never `fetch()`
19
+ * them — they are handed back verbatim and `release()` is a no-op.
20
+ * - Otherwise the blob is fetched from the owner's PDS via
21
+ * `com.atproto.sync.getBlob`, cached, and exposed as an object URL that
22
+ * `release()` revokes.
23
+ *
24
+ * The caller only renders `url`; identity/PDS/CDN orchestration stays here.
25
+ */
26
+ export declare function getBlob(options: GetBlobOptions): {
27
+ promise: Promise<string>;
28
+ release: () => void;
29
+ };
30
+ /**
31
+ * Low-level PDS blob fetch when the DID and PDS endpoint are already known.
32
+ * Returns the raw {@link Blob}; callers manage object URLs themselves.
33
+ */
34
+ export declare function fetchBlob(options: {
35
+ blobCache: BlobCache;
36
+ did: string;
37
+ cid: string;
38
+ endpoint: string;
39
+ }): {
40
+ promise: Promise<Blob>;
41
+ release: () => void;
42
+ };
@@ -0,0 +1,53 @@
1
+ import { extractCidFromBlob, getBlobImageUrl } from "./utils/blob.js";
2
+ function getBlob(n) {
3
+ let r = n.preferCdn === !1 ? void 0 : getBlobImageUrl(n.blob);
4
+ if (r) return {
5
+ promise: Promise.resolve(r),
6
+ release: () => {}
7
+ };
8
+ let a = n.handleOrDid.trim(), o = extractCidFromBlob(n.blob);
9
+ if (!a) return failed(/* @__PURE__ */ Error("handleOrDid is required"));
10
+ if (!o) return failed(/* @__PURE__ */ Error("Blob CID is required"));
11
+ let s, c;
12
+ return {
13
+ promise: (async () => {
14
+ let e = a.startsWith("did:") ? a : await n.resolver.resolveHandle(a), t = await n.resolver.pdsEndpointForDid(e), r = n.blobCache.ensure(e, o, () => {
15
+ let n = new AbortController();
16
+ return {
17
+ promise: (async () => {
18
+ let r = await fetch(`${t}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(e)}&cid=${encodeURIComponent(o)}`, { signal: n.signal });
19
+ if (!r.ok) throw Error(`Blob fetch failed (${r.status})`);
20
+ return r.blob();
21
+ })(),
22
+ abort: () => n.abort()
23
+ };
24
+ });
25
+ c = r.release;
26
+ let i = await r.promise;
27
+ return s = URL.createObjectURL(i), s;
28
+ })(),
29
+ release: () => {
30
+ s &&= (URL.revokeObjectURL(s), void 0), c?.();
31
+ }
32
+ };
33
+ }
34
+ function fetchBlob(e) {
35
+ return e.blobCache.ensure(e.did, e.cid, () => {
36
+ let t = new AbortController();
37
+ return {
38
+ promise: (async () => {
39
+ let n = await fetch(`${e.endpoint}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(e.did)}&cid=${encodeURIComponent(e.cid)}`, { signal: t.signal });
40
+ if (!n.ok) throw Error(`Blob fetch failed (${n.status})`);
41
+ return n.blob();
42
+ })(),
43
+ abort: () => t.abort()
44
+ };
45
+ });
46
+ }
47
+ function failed(e) {
48
+ return {
49
+ promise: Promise.reject(e),
50
+ release: () => {}
51
+ };
52
+ }
53
+ export { fetchBlob, getBlob };
@@ -0,0 +1,155 @@
1
+ import type { DidDocument } from "@atcute/identity";
2
+ import { ServiceResolver } from "./client";
3
+ export interface DidCacheStats {
4
+ entriesByDid: number;
5
+ entriesByHandle: number;
6
+ inFlightHandles: number;
7
+ inFlightDocs: number;
8
+ inFlightPdsEndpoints: number;
9
+ handleLookupHits: number;
10
+ handleLookupMisses: number;
11
+ didLookupHits: number;
12
+ didLookupMisses: number;
13
+ handleCacheHits: number;
14
+ handleFetches: number;
15
+ handleJoins: number;
16
+ docCacheHits: number;
17
+ docFetches: number;
18
+ docJoins: number;
19
+ pdsCacheHits: number;
20
+ pdsFetches: number;
21
+ pdsJoins: number;
22
+ writes: number;
23
+ stableSnapshotReuses: number;
24
+ }
25
+ export interface DidCacheSnapshot {
26
+ did?: string;
27
+ handle?: string;
28
+ doc?: DidDocument;
29
+ pdsEndpoint?: string;
30
+ }
31
+ export declare class DidCache {
32
+ private byHandle;
33
+ private byDid;
34
+ private handlePromises;
35
+ private docPromises;
36
+ private pdsPromises;
37
+ private statsData;
38
+ getByHandle(handle: string | undefined): DidCacheSnapshot | undefined;
39
+ getByDid(did: string | undefined): DidCacheSnapshot | undefined;
40
+ memoize(entry: {
41
+ did: string;
42
+ handle?: string;
43
+ doc?: DidDocument;
44
+ pdsEndpoint?: string;
45
+ }): DidCacheSnapshot;
46
+ ensureHandle(resolver: ServiceResolver, handle: string): Promise<DidCacheSnapshot>;
47
+ ensureDidDoc(resolver: ServiceResolver, did: string): Promise<DidCacheSnapshot>;
48
+ ensurePdsEndpoint(resolver: ServiceResolver, did: string): Promise<DidCacheSnapshot>;
49
+ stats(): DidCacheStats;
50
+ resetStats(): void;
51
+ clear(): void;
52
+ }
53
+ interface EnsureResult {
54
+ promise: Promise<Blob>;
55
+ release: () => void;
56
+ }
57
+ export interface CacheBucketStats {
58
+ entries: number;
59
+ inFlight: number;
60
+ hits: number;
61
+ misses: number;
62
+ sets: number;
63
+ inFlightStarts: number;
64
+ inFlightJoins: number;
65
+ releases: number;
66
+ skipped?: number;
67
+ }
68
+ export declare class BlobCache {
69
+ private store;
70
+ private inFlight;
71
+ private statsData;
72
+ private key;
73
+ get(did?: string, cid?: string): Blob | undefined;
74
+ set(did: string, cid: string, blob: Blob): void;
75
+ ensure(did: string, cid: string, loader: () => {
76
+ promise: Promise<Blob>;
77
+ abort: () => void;
78
+ }): EnsureResult;
79
+ private release;
80
+ stats(): CacheBucketStats;
81
+ resetStats(): void;
82
+ clear(): void;
83
+ }
84
+ interface RecordEnsureResult<T = unknown> {
85
+ promise: Promise<T>;
86
+ release: () => void;
87
+ }
88
+ export declare class RecordCache {
89
+ private store;
90
+ private inFlight;
91
+ private statsData;
92
+ private noCacheCollections;
93
+ private key;
94
+ private shouldCache;
95
+ get<T = unknown>(did?: string, collection?: string, rkey?: string): T | undefined;
96
+ set<T = unknown>(did: string, collection: string, rkey: string, record: T): void;
97
+ ensure<T = unknown>(did: string, collection: string, rkey: string, loader: () => {
98
+ promise: Promise<T>;
99
+ abort: () => void;
100
+ }): RecordEnsureResult<T>;
101
+ private release;
102
+ stats(): CacheBucketStats;
103
+ resetStats(): void;
104
+ clear(): void;
105
+ }
106
+ interface PageEnsureResult<T = unknown> {
107
+ promise: Promise<T>;
108
+ release: () => void;
109
+ }
110
+ export declare class PaginatedRecordsCache {
111
+ private store;
112
+ private inFlight;
113
+ private statsData;
114
+ get<T = unknown>(key: string | undefined): T | undefined;
115
+ set<T = unknown>(key: string, page: T): void;
116
+ ensure<T = unknown>(key: string, loader: () => Promise<T>): PageEnsureResult<T>;
117
+ private release;
118
+ stats(): CacheBucketStats;
119
+ resetStats(): void;
120
+ clear(): void;
121
+ }
122
+ /**
123
+ * Library-owned cache singletons.
124
+ *
125
+ * Caches are config-free and keyed by universal identifiers (DID, CID, at-uri),
126
+ * so they are shared across the entire app and—critically—survive component and
127
+ * provider remounts. Cache lifetime must NOT be tied to component lifecycle; a
128
+ * remounting provider should reuse these, not recreate them, or in-flight and
129
+ * cached data is lost on every remount.
130
+ */
131
+ export interface AtprotoCaches {
132
+ didCache: DidCache;
133
+ blobCache: BlobCache;
134
+ recordCache: RecordCache;
135
+ paginatedRecordsCache: PaginatedRecordsCache;
136
+ }
137
+ export interface AtprotoCacheStats {
138
+ did: DidCacheStats;
139
+ blob: CacheBucketStats;
140
+ record: CacheBucketStats;
141
+ paginatedRecords: CacheBucketStats;
142
+ }
143
+ export declare const sharedDidCache: DidCache;
144
+ export declare const sharedBlobCache: BlobCache;
145
+ export declare const sharedRecordCache: RecordCache;
146
+ export declare const sharedPaginatedRecordsCache: PaginatedRecordsCache;
147
+ /** Returns the shared, library-owned cache instances. */
148
+ export declare function getSharedCaches(): AtprotoCaches;
149
+ /** Returns point-in-time counters and sizes for the shared cache singletons. */
150
+ export declare function getSharedCacheStats(): AtprotoCacheStats;
151
+ /** Resets counters only. Cached entries remain available for warm-run benchmarks. */
152
+ export declare function resetSharedCacheStats(): void;
153
+ /** Clears entries and in-flight maps from the shared cache singletons. */
154
+ export declare function clearSharedCaches(): void;
155
+ export {};
package/dist/cache.js ADDED
@@ -0,0 +1,345 @@
1
+ var createDidStats = () => ({
2
+ handleLookupHits: 0,
3
+ handleLookupMisses: 0,
4
+ didLookupHits: 0,
5
+ didLookupMisses: 0,
6
+ handleCacheHits: 0,
7
+ handleFetches: 0,
8
+ handleJoins: 0,
9
+ docCacheHits: 0,
10
+ docFetches: 0,
11
+ docJoins: 0,
12
+ pdsCacheHits: 0,
13
+ pdsFetches: 0,
14
+ pdsJoins: 0,
15
+ writes: 0,
16
+ stableSnapshotReuses: 0
17
+ }), toSnapshot = (e) => {
18
+ if (!e) return;
19
+ if (e.snapshot) return e.snapshot;
20
+ let { did: o, handle: s, doc: c, pdsEndpoint: l } = e, u = {
21
+ did: o,
22
+ handle: s,
23
+ doc: c,
24
+ pdsEndpoint: l
25
+ };
26
+ return e.snapshot = u, u;
27
+ }, derivePdsEndpoint = (e) => {
28
+ if (!e?.service) return;
29
+ let o = e.service.find((e) => e.type === "AtprotoPersonalDataServer");
30
+ if (!o) return;
31
+ let s = typeof o.serviceEndpoint == "string" ? o.serviceEndpoint : void 0;
32
+ if (s) return s.replace(/\/$/, "");
33
+ }, DidCache = class {
34
+ constructor() {
35
+ this.byHandle = /* @__PURE__ */ new Map(), this.byDid = /* @__PURE__ */ new Map(), this.handlePromises = /* @__PURE__ */ new Map(), this.docPromises = /* @__PURE__ */ new Map(), this.pdsPromises = /* @__PURE__ */ new Map(), this.statsData = createDidStats();
36
+ }
37
+ getByHandle(e) {
38
+ if (!e) return;
39
+ let s = this.byHandle.get(e.toLowerCase());
40
+ return s ? this.statsData.handleLookupHits += 1 : this.statsData.handleLookupMisses += 1, toSnapshot(s);
41
+ }
42
+ getByDid(e) {
43
+ if (!e) return;
44
+ let s = this.byDid.get(e);
45
+ return s ? this.statsData.didLookupHits += 1 : this.statsData.didLookupMisses += 1, toSnapshot(s);
46
+ }
47
+ memoize(e) {
48
+ let c = e.did, l = e.handle?.toLowerCase(), u = this.byDid.get(c) ?? (l ? this.byHandle.get(l) : void 0), d = e.doc ?? u?.doc, f = l ?? u?.handle, p = e.pdsEndpoint ?? derivePdsEndpoint(d) ?? u?.pdsEndpoint;
49
+ if (u && u.did === c && u.handle === f && u.doc === d && u.pdsEndpoint === p) return this.statsData.stableSnapshotReuses += 1, toSnapshot(u);
50
+ let m = {
51
+ did: c,
52
+ handle: f,
53
+ doc: d,
54
+ pdsEndpoint: p,
55
+ timestamp: Date.now(),
56
+ snapshot: void 0
57
+ };
58
+ return this.byDid.set(c, m), f && this.byHandle.set(f, m), this.statsData.writes += 1, toSnapshot(m);
59
+ }
60
+ ensureHandle(e, o) {
61
+ let s = o.toLowerCase(), c = this.getByHandle(s);
62
+ if (c?.did) return this.statsData.handleCacheHits += 1, Promise.resolve(c);
63
+ let l = this.handlePromises.get(s);
64
+ if (l) return this.statsData.handleJoins += 1, l;
65
+ this.statsData.handleFetches += 1;
66
+ let u = e.resolveHandle(s).then((e) => this.memoize({
67
+ did: e,
68
+ handle: s
69
+ })).finally(() => {
70
+ this.handlePromises.delete(s);
71
+ });
72
+ return this.handlePromises.set(s, u), u;
73
+ }
74
+ ensureDidDoc(e, o) {
75
+ let s = this.getByDid(o);
76
+ if (s?.doc) return this.statsData.docCacheHits += 1, Promise.resolve(s);
77
+ let c = this.docPromises.get(o);
78
+ if (c) return this.statsData.docJoins += 1, c;
79
+ this.statsData.docFetches += 1;
80
+ let l = e.resolveDidDoc(o).then((e) => {
81
+ let c = e.alsoKnownAs?.find((e) => e.startsWith("at://")), l = c ? c.replace("at://", "").toLowerCase() : s?.handle;
82
+ return this.memoize({
83
+ did: o,
84
+ handle: l,
85
+ doc: e
86
+ });
87
+ }).finally(() => {
88
+ this.docPromises.delete(o);
89
+ });
90
+ return this.docPromises.set(o, l), l;
91
+ }
92
+ ensurePdsEndpoint(e, o) {
93
+ let s = this.getByDid(o);
94
+ if (s?.pdsEndpoint) return this.statsData.pdsCacheHits += 1, Promise.resolve(s);
95
+ let c = this.pdsPromises.get(o);
96
+ if (c) return this.statsData.pdsJoins += 1, c;
97
+ this.statsData.pdsFetches += 1;
98
+ let l = (async () => {
99
+ let s = await this.ensureDidDoc(e, o).catch(() => void 0);
100
+ if (s?.pdsEndpoint) return s;
101
+ let c = await e.pdsEndpointForDid(o);
102
+ return this.memoize({
103
+ did: o,
104
+ pdsEndpoint: c
105
+ });
106
+ })().finally(() => {
107
+ this.pdsPromises.delete(o);
108
+ });
109
+ return this.pdsPromises.set(o, l), l;
110
+ }
111
+ stats() {
112
+ return {
113
+ ...this.statsData,
114
+ entriesByDid: this.byDid.size,
115
+ entriesByHandle: this.byHandle.size,
116
+ inFlightHandles: this.handlePromises.size,
117
+ inFlightDocs: this.docPromises.size,
118
+ inFlightPdsEndpoints: this.pdsPromises.size
119
+ };
120
+ }
121
+ resetStats() {
122
+ this.statsData = createDidStats();
123
+ }
124
+ clear() {
125
+ this.byHandle.clear(), this.byDid.clear(), this.handlePromises.clear(), this.docPromises.clear(), this.pdsPromises.clear();
126
+ }
127
+ }, createBucketStats = () => ({
128
+ hits: 0,
129
+ misses: 0,
130
+ sets: 0,
131
+ inFlightStarts: 0,
132
+ inFlightJoins: 0,
133
+ releases: 0,
134
+ skipped: 0
135
+ }), BlobCache = class {
136
+ constructor() {
137
+ this.store = /* @__PURE__ */ new Map(), this.inFlight = /* @__PURE__ */ new Map(), this.statsData = createBucketStats();
138
+ }
139
+ key(e, o) {
140
+ return `${e}::${o}`;
141
+ }
142
+ get(e, o) {
143
+ if (!e || !o) return;
144
+ let s = this.store.get(this.key(e, o));
145
+ return s ? this.statsData.hits += 1 : this.statsData.misses += 1, s?.blob;
146
+ }
147
+ set(e, o, s) {
148
+ this.store.set(this.key(e, o), {
149
+ blob: s,
150
+ timestamp: Date.now()
151
+ }), this.statsData.sets += 1;
152
+ }
153
+ ensure(e, o, s) {
154
+ let c = this.get(e, o);
155
+ if (c) return {
156
+ promise: Promise.resolve(c),
157
+ release: () => {}
158
+ };
159
+ let l = this.key(e, o), u = this.inFlight.get(l);
160
+ if (u) return u.refCount += 1, this.statsData.inFlightJoins += 1, {
161
+ promise: u.promise,
162
+ release: () => this.release(l)
163
+ };
164
+ let { promise: d, abort: f } = s();
165
+ this.statsData.inFlightStarts += 1;
166
+ let p = d.then((s) => (this.set(e, o, s), s)), m = {
167
+ promise: p,
168
+ abort: f,
169
+ refCount: 1
170
+ };
171
+ return this.inFlight.set(l, m), p.catch(() => {}).finally(() => {
172
+ this.inFlight.delete(l);
173
+ }), {
174
+ promise: p,
175
+ release: () => this.release(l)
176
+ };
177
+ }
178
+ release(e) {
179
+ let o = this.inFlight.get(e);
180
+ o && (--o.refCount, this.statsData.releases += 1);
181
+ }
182
+ stats() {
183
+ return {
184
+ ...this.statsData,
185
+ entries: this.store.size,
186
+ inFlight: this.inFlight.size
187
+ };
188
+ }
189
+ resetStats() {
190
+ this.statsData = createBucketStats();
191
+ }
192
+ clear() {
193
+ this.store.clear(), this.inFlight.clear();
194
+ }
195
+ }, RecordCache = class {
196
+ constructor() {
197
+ this.store = /* @__PURE__ */ new Map(), this.inFlight = /* @__PURE__ */ new Map(), this.statsData = createBucketStats(), this.noCacheCollections = new Set(["fm.teal.alpha.actor.status", "fm.teal.alpha.feed.play"]);
198
+ }
199
+ key(e, o, s) {
200
+ return `${e}::${o}::${s}`;
201
+ }
202
+ shouldCache(e) {
203
+ return !this.noCacheCollections.has(e);
204
+ }
205
+ get(e, o, s) {
206
+ if (!e || !o || !s) return;
207
+ if (!this.shouldCache(o)) {
208
+ this.statsData.skipped = (this.statsData.skipped ?? 0) + 1;
209
+ return;
210
+ }
211
+ let c = this.store.get(this.key(e, o, s));
212
+ return c ? this.statsData.hits += 1 : this.statsData.misses += 1, c?.record;
213
+ }
214
+ set(e, o, s, c) {
215
+ if (!this.shouldCache(o)) {
216
+ this.statsData.skipped = (this.statsData.skipped ?? 0) + 1;
217
+ return;
218
+ }
219
+ this.store.set(this.key(e, o, s), {
220
+ record: c,
221
+ timestamp: Date.now()
222
+ }), this.statsData.sets += 1;
223
+ }
224
+ ensure(e, o, s, c) {
225
+ let l = this.get(e, o, s);
226
+ if (l !== void 0) return {
227
+ promise: Promise.resolve(l),
228
+ release: () => {}
229
+ };
230
+ let u = this.key(e, o, s), d = this.inFlight.get(u);
231
+ if (d) return d.refCount += 1, this.statsData.inFlightJoins += 1, {
232
+ promise: d.promise,
233
+ release: () => this.release(u)
234
+ };
235
+ let { promise: f, abort: p } = c();
236
+ this.statsData.inFlightStarts += 1;
237
+ let m = f.then((c) => (this.set(e, o, s, c), c)), h = {
238
+ promise: m,
239
+ abort: p,
240
+ refCount: 1
241
+ };
242
+ return this.inFlight.set(u, h), m.catch(() => {}).finally(() => {
243
+ this.inFlight.delete(u);
244
+ }), {
245
+ promise: m,
246
+ release: () => this.release(u)
247
+ };
248
+ }
249
+ release(e) {
250
+ let o = this.inFlight.get(e);
251
+ o && (--o.refCount, this.statsData.releases += 1);
252
+ }
253
+ stats() {
254
+ return {
255
+ ...this.statsData,
256
+ entries: this.store.size,
257
+ inFlight: this.inFlight.size
258
+ };
259
+ }
260
+ resetStats() {
261
+ this.statsData = createBucketStats();
262
+ }
263
+ clear() {
264
+ this.store.clear(), this.inFlight.clear();
265
+ }
266
+ }, PaginatedRecordsCache = class {
267
+ constructor() {
268
+ this.store = /* @__PURE__ */ new Map(), this.inFlight = /* @__PURE__ */ new Map(), this.statsData = createBucketStats();
269
+ }
270
+ get(e) {
271
+ if (!e) return;
272
+ let o = this.store.get(e);
273
+ return o ? this.statsData.hits += 1 : this.statsData.misses += 1, o?.page;
274
+ }
275
+ set(e, o) {
276
+ this.store.set(e, {
277
+ page: o,
278
+ timestamp: Date.now()
279
+ }), this.statsData.sets += 1;
280
+ }
281
+ ensure(e, o) {
282
+ let s = this.get(e);
283
+ if (s !== void 0) return {
284
+ promise: Promise.resolve(s),
285
+ release: () => {}
286
+ };
287
+ let c = this.inFlight.get(e);
288
+ if (c) return c.refCount += 1, this.statsData.inFlightJoins += 1, {
289
+ promise: c.promise,
290
+ release: () => this.release(e)
291
+ };
292
+ this.statsData.inFlightStarts += 1;
293
+ let l = o().then((o) => (this.set(e, o), o));
294
+ return this.inFlight.set(e, {
295
+ promise: l,
296
+ refCount: 1
297
+ }), l.catch(() => {}).finally(() => {
298
+ this.inFlight.delete(e);
299
+ }), {
300
+ promise: l,
301
+ release: () => this.release(e)
302
+ };
303
+ }
304
+ release(e) {
305
+ let o = this.inFlight.get(e);
306
+ o && (--o.refCount, this.statsData.releases += 1);
307
+ }
308
+ stats() {
309
+ return {
310
+ ...this.statsData,
311
+ entries: this.store.size,
312
+ inFlight: this.inFlight.size
313
+ };
314
+ }
315
+ resetStats() {
316
+ this.statsData = createBucketStats();
317
+ }
318
+ clear() {
319
+ this.store.clear(), this.inFlight.clear();
320
+ }
321
+ };
322
+ const sharedDidCache = new DidCache(), sharedBlobCache = new BlobCache(), sharedRecordCache = new RecordCache(), sharedPaginatedRecordsCache = new PaginatedRecordsCache();
323
+ function getSharedCaches() {
324
+ return {
325
+ didCache: sharedDidCache,
326
+ blobCache: sharedBlobCache,
327
+ recordCache: sharedRecordCache,
328
+ paginatedRecordsCache: sharedPaginatedRecordsCache
329
+ };
330
+ }
331
+ function getSharedCacheStats() {
332
+ return {
333
+ did: sharedDidCache.stats(),
334
+ blob: sharedBlobCache.stats(),
335
+ record: sharedRecordCache.stats(),
336
+ paginatedRecords: sharedPaginatedRecordsCache.stats()
337
+ };
338
+ }
339
+ function resetSharedCacheStats() {
340
+ sharedDidCache.resetStats(), sharedBlobCache.resetStats(), sharedRecordCache.resetStats(), sharedPaginatedRecordsCache.resetStats();
341
+ }
342
+ function clearSharedCaches() {
343
+ sharedDidCache.clear(), sharedBlobCache.clear(), sharedRecordCache.clear(), sharedPaginatedRecordsCache.clear();
344
+ }
345
+ export { BlobCache, DidCache, PaginatedRecordsCache, RecordCache, clearSharedCaches, getSharedCacheStats, getSharedCaches, resetSharedCacheStats, sharedBlobCache, sharedDidCache, sharedPaginatedRecordsCache, sharedRecordCache };
@@ -0,0 +1,45 @@
1
+ import { Client } from "@atcute/client";
2
+ import type { DidDocument } from "@atcute/identity";
3
+ export interface ServiceResolverOptions {
4
+ plcDirectory?: string;
5
+ identityService?: string;
6
+ slingshotBaseUrl?: string;
7
+ fetch?: typeof fetch;
8
+ }
9
+ /**
10
+ * Default configuration values for AT Protocol services.
11
+ * These can be overridden via AtProtoProvider props.
12
+ */
13
+ export declare const DEFAULT_CONFIG: {
14
+ readonly plcDirectory: "https://plc.directory";
15
+ readonly identityService: "https://public.api.bsky.app";
16
+ readonly slingshotBaseUrl: "https://slingshot.microcosm.blue";
17
+ readonly blueskyAppviewService: "https://public.api.bsky.app";
18
+ readonly blueskyAppBaseUrl: "https://bsky.app";
19
+ readonly tangledBaseUrl: "https://tangled.org";
20
+ readonly constellationBaseUrl: "https://constellation.microcosm.blue";
21
+ };
22
+ export declare const SLINGSHOT_BASE_URL = "https://slingshot.microcosm.blue";
23
+ export declare const normalizeBaseUrl: (input: string) => string;
24
+ export declare class ServiceResolver {
25
+ private plc;
26
+ private slingshot;
27
+ private didResolver;
28
+ private handleResolver;
29
+ private fetchImpl;
30
+ constructor(opts?: ServiceResolverOptions);
31
+ resolveDidDoc(did: string): Promise<DidDocument>;
32
+ pdsEndpointForDid(did: string): Promise<string>;
33
+ getSlingshotUrl(): string;
34
+ resolveHandle(handle: string): Promise<string>;
35
+ }
36
+ export interface CreateClientOptions extends ServiceResolverOptions {
37
+ did?: string;
38
+ service?: string;
39
+ }
40
+ export declare function createAtprotoClient(opts?: CreateClientOptions): Promise<{
41
+ rpc: Client<import("@atcute/lexicons/ambient").XRPCQueries, import("@atcute/lexicons/ambient").XRPCProcedures>;
42
+ service: string;
43
+ resolver: ServiceResolver;
44
+ }>;
45
+ export type AtprotoClient = Awaited<ReturnType<typeof createAtprotoClient>>["rpc"];