@atproto/bsky 0.3.12 → 0.4.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.
- package/CHANGELOG.md +43 -0
- package/dist/api/app/bsky/notification/getGroupedNotifications.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/getGroupedNotifications.js +14 -3
- package/dist/api/app/bsky/notification/getGroupedNotifications.js.map +1 -1
- package/dist/api/blob-resolver.d.ts.map +1 -1
- package/dist/api/blob-resolver.js +9 -8
- package/dist/api/blob-resolver.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -6
- package/dist/config.js.map +1 -1
- package/dist/hydration/external.d.ts +52 -45
- package/dist/hydration/external.d.ts.map +1 -1
- package/dist/hydration/external.js +132 -114
- package/dist/hydration/external.js.map +1 -1
- package/dist/hydration/hydrator.d.ts +10 -8
- package/dist/hydration/hydrator.d.ts.map +1 -1
- package/dist/hydration/hydrator.js +82 -189
- package/dist/hydration/hydrator.js.map +1 -1
- package/dist/image/server.d.ts.map +1 -1
- package/dist/image/server.js +12 -1
- package/dist/image/server.js.map +1 -1
- package/dist/lexicons/tools/ozone/moderation/emitEvent.defs.d.ts +2 -2
- package/dist/lexicons/tools/ozone/moderation/emitEvent.defs.js.map +1 -1
- package/dist/lexicons/tools/ozone/report/closeReports.defs.d.ts +1 -1
- package/dist/lexicons/tools/ozone/report/closeReports.defs.d.ts.map +1 -1
- package/dist/lexicons/tools/ozone/report/closeReports.defs.js +1 -1
- package/dist/lexicons/tools/ozone/report/closeReports.defs.js.map +1 -1
- package/dist/telemetry/events.d.ts +11 -4
- package/dist/telemetry/events.d.ts.map +1 -1
- package/dist/telemetry/events.js +29 -5
- package/dist/telemetry/events.js.map +1 -1
- package/dist/util/external/standard-site.d.ts +2 -3
- package/dist/util/external/standard-site.d.ts.map +1 -1
- package/dist/util/external/standard-site.js +2 -3
- package/dist/util/external/standard-site.js.map +1 -1
- package/dist/util/uris.d.ts +5 -2
- package/dist/util/uris.d.ts.map +1 -1
- package/dist/util/uris.js +14 -1
- package/dist/util/uris.js.map +1 -1
- package/dist/views/index.d.ts +2 -2
- package/dist/views/index.d.ts.map +1 -1
- package/dist/views/index.js +17 -11
- package/dist/views/index.js.map +1 -1
- package/package.json +11 -12
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
import { performance } from 'node:perf_hooks';
|
|
2
2
|
import { chunkArray } from '@atproto/common';
|
|
3
|
-
import { isTypedLexMap, lexParseJsonBytes, } from '@atproto/lex';
|
|
3
|
+
import { isDidString, isTypedLexMap, lexParseJsonBytes, } from '@atproto/lex';
|
|
4
4
|
import { parseAtUriString } from '@atproto/syntax';
|
|
5
|
-
import { site } from '../lexicons/index.js';
|
|
6
|
-
import { hydrationLogger } from '../logger.js';
|
|
5
|
+
import { site, social } from '../lexicons/index.js';
|
|
7
6
|
import { RecordLookupStatus, } from '../proto/bsky_pb.js';
|
|
8
7
|
import { events, } from '../telemetry/events.js';
|
|
9
|
-
import { uriToDid } from '../util/uris.js';
|
|
8
|
+
import { recordUriOf, uriToDid } from '../util/uris.js';
|
|
9
|
+
import { isExternalEmbedType, isRecordWithMediaType, } from '../views/types.js';
|
|
10
10
|
import { HydrationMap, parseDate, safeTakedownRef, } from './util.js';
|
|
11
|
+
/**
|
|
12
|
+
* Per-collection sample limits. Basic hydration skips social previews,
|
|
13
|
+
* but keeps gallery items. Direct publication/photo references are singular.
|
|
14
|
+
*/
|
|
15
|
+
export const EXTERNAL_HYDRATION_LIMITS = {
|
|
16
|
+
[site.standard.document.$type]: { recommendationPreviews: 3 },
|
|
17
|
+
[site.standard.publication.$type]: { subscriberPreviews: 3 },
|
|
18
|
+
[social.grain.gallery.$type]: { items: 10, favoritePreviews: 3 },
|
|
19
|
+
};
|
|
20
|
+
/** Maximum in-flight backlink sample RPCs, not a dependency-count limit. */
|
|
21
|
+
export const MAX_CONCURRENT_BACKLINK_REQUESTS = 8;
|
|
22
|
+
/** Maximum logical batches per traversal, counting the root as pass 1. */
|
|
23
|
+
export const MAX_EXTERNAL_HYDRATION_PASSES = 8;
|
|
11
24
|
export class ExternalHydrator {
|
|
12
|
-
static { this.MAX_BACKLINK_PREVIEWS = 3; }
|
|
13
|
-
static { this.MAX_BACKLINK_GALLERY_ITEMS = 10; }
|
|
14
|
-
static { this.MAX_BACKLINK_FANOUT = 8; }
|
|
15
|
-
/** Hard cap on generic batches per traversal, counting the root as pass 1. */
|
|
16
|
-
static { this.MAX_EXTERNAL_HYDRATION_PASSES = 8; }
|
|
17
25
|
/**
|
|
18
26
|
* Entries per generic record RPC. The dataplane rejects larger URI lookups;
|
|
19
27
|
* exact-ref lookups use the same bound defensively.
|
|
20
28
|
*/
|
|
21
29
|
static { this.MAX_RECORDS_PER_REQUEST = 100; }
|
|
30
|
+
/**
|
|
31
|
+
* Target URIs per backlink-count RPC. The dataplane requires between 1 and
|
|
32
|
+
* 100; this is a separate limit from `MAX_RECORDS_PER_REQUEST`.
|
|
33
|
+
*/
|
|
34
|
+
static { this.MAX_BACKLINK_COUNT_TARGETS_PER_REQUEST = 100; }
|
|
22
35
|
constructor(dataplane) {
|
|
23
36
|
this.dataplane = dataplane;
|
|
24
37
|
}
|
|
@@ -68,20 +81,24 @@ export class ExternalHydrator {
|
|
|
68
81
|
cursor: res.cursor || undefined,
|
|
69
82
|
};
|
|
70
83
|
}
|
|
71
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Fetch source-record counts, optionally restricted to one collection. Large
|
|
86
|
+
* inputs are fetched in sequential bounded requests; any failed request
|
|
87
|
+
* rejects the whole call.
|
|
88
|
+
*/
|
|
72
89
|
async getAtmosphereBacklinkCounts(targetUris, collection) {
|
|
73
90
|
const map = new HydrationMap();
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
for (const chunk of chunkArray(targetUris, ExternalHydrator.MAX_BACKLINK_COUNT_TARGETS_PER_REQUEST)) {
|
|
92
|
+
const res = await this.dataplane.getAtmosphereBacklinkCounts({
|
|
93
|
+
targetUris: chunk,
|
|
94
|
+
collection,
|
|
95
|
+
});
|
|
96
|
+
for (const result of res.results) {
|
|
97
|
+
map.set(result.targetUri, Object.fromEntries(Object.entries(result.counts).map(([nsid, count]) => [
|
|
98
|
+
nsid,
|
|
99
|
+
Number(count),
|
|
100
|
+
])));
|
|
101
|
+
}
|
|
85
102
|
}
|
|
86
103
|
return map;
|
|
87
104
|
}
|
|
@@ -115,21 +132,6 @@ export class ExternalHydrator {
|
|
|
115
132
|
}
|
|
116
133
|
return { backlinks, truncated: res.truncated };
|
|
117
134
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Fetch exact `site.standard.{document,publication}` versions. Does NOT
|
|
120
|
-
* resolve a document's publication; callers must pin it explicitly.
|
|
121
|
-
*/
|
|
122
|
-
async getSiteStandardRecordsByRef(refs, includeTakedowns = false) {
|
|
123
|
-
const ssRefs = refs.filter((ref) => !!ref.cid && siteStandardKind(ref.uri) !== undefined);
|
|
124
|
-
const out = emptySiteStandardRecords();
|
|
125
|
-
if (!ssRefs.length)
|
|
126
|
-
return out;
|
|
127
|
-
const records = await this.getRecordsByRef(ssRefs, includeTakedowns);
|
|
128
|
-
for (const [key, info] of records) {
|
|
129
|
-
setSiteStandardRecord(out, parseGenericRecordKey(key).uri, key, info);
|
|
130
|
-
}
|
|
131
|
-
return out;
|
|
132
|
-
}
|
|
133
135
|
}
|
|
134
136
|
export function genericRecordKey({ uri, cid }) {
|
|
135
137
|
if (cid === undefined)
|
|
@@ -180,9 +182,10 @@ export function actionExternalRecordTakedowns({ externalRecords, externalRecords
|
|
|
180
182
|
* there is none. Only the owner reports, once, including on failure, with the
|
|
181
183
|
* context's XRPC endpoint (`'unknown'` if unset) and the traversal's wall time. A
|
|
182
184
|
* returned traversal is `capped` if it hit the pass limit, else `partial` if
|
|
183
|
-
* it recovered from a dependency failure. Owners that completed
|
|
184
|
-
*
|
|
185
|
-
* are.
|
|
185
|
+
* it recovered from a dependency failure. Owners that completed report when
|
|
186
|
+
* they scheduled a batch or inspected roots (even prehydrated or unavailable
|
|
187
|
+
* ones); only genuinely empty successful calls are not reported. Other
|
|
188
|
+
* outcomes always are.
|
|
186
189
|
*/
|
|
187
190
|
export async function trackExternalHydration(root, xrpcEndpoint, hydrationMetrics, fn) {
|
|
188
191
|
if (hydrationMetrics)
|
|
@@ -207,7 +210,7 @@ export async function trackExternalHydration(root, xrpcEndpoint, hydrationMetric
|
|
|
207
210
|
return state;
|
|
208
211
|
}
|
|
209
212
|
finally {
|
|
210
|
-
if (owned.batches || outcome !== 'completed') {
|
|
213
|
+
if (owned.batches > 0 || owned.roots > 0 || outcome !== 'completed') {
|
|
211
214
|
events.externalHydrationTraversal({
|
|
212
215
|
xrpcEndpoint: xrpcEndpoint ?? 'unknown',
|
|
213
216
|
root,
|
|
@@ -251,10 +254,6 @@ function parseGenericRecord(result, includeTakedowns) {
|
|
|
251
254
|
takedownRef: safeTakedownRef(entry),
|
|
252
255
|
};
|
|
253
256
|
}
|
|
254
|
-
const emptySiteStandardRecords = () => ({
|
|
255
|
-
documents: new HydrationMap(),
|
|
256
|
-
publications: new HydrationMap(),
|
|
257
|
-
});
|
|
258
257
|
const siteStandardKind = (uri) => {
|
|
259
258
|
const parsed = parseAtUriString(uri);
|
|
260
259
|
if (!parsed.success)
|
|
@@ -277,82 +276,101 @@ const matchRecordInfo = (schema, info) => info && schema.$matches(info.record, {
|
|
|
277
276
|
? { ...info, record: info.record }
|
|
278
277
|
: null;
|
|
279
278
|
/**
|
|
280
|
-
*
|
|
281
|
-
*
|
|
279
|
+
* Select one pinned embed root. Legacy publication-first pairs may put their
|
|
280
|
+
* document second; no other authored refs participate in hydration.
|
|
282
281
|
*/
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
282
|
+
export function getExternalRootRef(refs) {
|
|
283
|
+
let root = refs?.[0];
|
|
284
|
+
if (!root)
|
|
285
|
+
return undefined;
|
|
286
|
+
if (siteStandardKind(root.uri) === 'publications') {
|
|
287
|
+
const second = refs?.[1];
|
|
288
|
+
if (second && siteStandardKind(second.uri) === 'documents')
|
|
289
|
+
root = second;
|
|
291
290
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
* - Only a publication referenced: document stays `undefined`.
|
|
307
|
-
*
|
|
308
|
-
* Each returned slot carries the matching `ref` so callers can recover
|
|
309
|
-
* the owner DID for blob-cdn URL building, etc.
|
|
310
|
-
*/
|
|
311
|
-
export const getSiteStandardRecordsFromHydrationMapsByRefs = (associatedRefs, documents, publications) => {
|
|
312
|
-
if (!associatedRefs?.length) {
|
|
313
|
-
return { document: undefined, publication: undefined };
|
|
291
|
+
return isHydratableAssociatedRef(root) ? root : undefined;
|
|
292
|
+
}
|
|
293
|
+
/** Select a pinned Standard.site root and its derived publication, if needed. */
|
|
294
|
+
export function getSiteStandardRecordsFromRefs(associatedRefs, records, dependencies) {
|
|
295
|
+
const empty = { document: undefined, publication: undefined };
|
|
296
|
+
const ref = getExternalRootRef(associatedRefs);
|
|
297
|
+
if (!ref)
|
|
298
|
+
return empty;
|
|
299
|
+
const info = records?.get(genericRecordKey(ref));
|
|
300
|
+
if (siteStandardKind(ref.uri) === 'publications') {
|
|
301
|
+
const publication = matchRecordInfo(site.standard.publication.main, info);
|
|
302
|
+
return publication
|
|
303
|
+
? { document: undefined, publication: { ref, info: publication } }
|
|
304
|
+
: empty;
|
|
314
305
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
document = { ref, info: hit };
|
|
325
|
-
}
|
|
326
|
-
if (!publication) {
|
|
327
|
-
const hit = publications?.get(key);
|
|
328
|
-
if (hit)
|
|
329
|
-
publication = { ref, info: hit };
|
|
330
|
-
}
|
|
331
|
-
if (document && publication)
|
|
332
|
-
break;
|
|
306
|
+
if (siteStandardKind(ref.uri) !== 'documents')
|
|
307
|
+
return empty;
|
|
308
|
+
const document = matchRecordInfo(site.standard.document.main, info);
|
|
309
|
+
if (!document)
|
|
310
|
+
return empty;
|
|
311
|
+
const documentSelection = { ref, info: document };
|
|
312
|
+
const docSite = document.record.site;
|
|
313
|
+
if (!docSite.startsWith('at://')) {
|
|
314
|
+
return { document: documentSelection, publication: undefined };
|
|
333
315
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
316
|
+
const uri = recordUriOf(docSite, site.standard.publication.$type);
|
|
317
|
+
if (!uri)
|
|
318
|
+
return empty;
|
|
319
|
+
const publication = matchRecordInfo(site.standard.publication.main, dependencies?.get(uri));
|
|
320
|
+
if (!publication)
|
|
321
|
+
return empty;
|
|
322
|
+
return {
|
|
323
|
+
document: documentSelection,
|
|
324
|
+
publication: { ref: { uri, cid: publication.cid }, info: publication },
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
const externalAssociatedRefs = (post) => {
|
|
328
|
+
const embed = post?.embed;
|
|
329
|
+
if (!embed)
|
|
330
|
+
return [];
|
|
331
|
+
if (isExternalEmbedType(embed)) {
|
|
332
|
+
return embed.external.associatedRefs ?? [];
|
|
346
333
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
334
|
+
if (isRecordWithMediaType(embed) && isExternalEmbedType(embed.media)) {
|
|
335
|
+
return embed.media.external.associatedRefs ?? [];
|
|
336
|
+
}
|
|
337
|
+
return [];
|
|
338
|
+
};
|
|
339
|
+
/**
|
|
340
|
+
* An exact ref needs a nonempty CID and a complete record URI in a DID repo,
|
|
341
|
+
* so malformed refs are skipped instead of becoming latest lookups.
|
|
342
|
+
*/
|
|
343
|
+
const isHydratableAssociatedRef = (ref) => {
|
|
344
|
+
if (!ref.cid)
|
|
345
|
+
return false;
|
|
346
|
+
const parsed = parseAtUriString(ref.uri);
|
|
347
|
+
if (!parsed.success)
|
|
348
|
+
return false;
|
|
349
|
+
const { authority, collection, rkey, query, hash } = parsed.value;
|
|
350
|
+
return (isDidString(authority) &&
|
|
351
|
+
!!collection &&
|
|
352
|
+
!!rkey &&
|
|
353
|
+
query === undefined &&
|
|
354
|
+
hash === undefined);
|
|
355
|
+
};
|
|
356
|
+
/** Collect one root per post across all layers, deduped by exact ref. */
|
|
357
|
+
export const externalAssociatedRefsFromPosts = (...postLayers) => {
|
|
358
|
+
const seen = new Set();
|
|
359
|
+
const out = [];
|
|
360
|
+
for (const layer of postLayers) {
|
|
361
|
+
for (const item of layer.values()) {
|
|
362
|
+
if (!item)
|
|
363
|
+
continue;
|
|
364
|
+
const ref = getExternalRootRef(externalAssociatedRefs(item.record));
|
|
365
|
+
if (!ref)
|
|
366
|
+
continue;
|
|
367
|
+
const key = genericRecordKey(ref);
|
|
368
|
+
if (seen.has(key))
|
|
369
|
+
continue;
|
|
370
|
+
seen.add(key);
|
|
371
|
+
out.push({ uri: ref.uri, cid: ref.cid });
|
|
354
372
|
}
|
|
355
373
|
}
|
|
356
|
-
return
|
|
374
|
+
return out;
|
|
357
375
|
};
|
|
358
376
|
//# sourceMappingURL=external.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external.js","sourceRoot":"","sources":["../../src/hydration/external.ts"],"names":[],"mappings":"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","sourcesContent":["import { performance } from 'node:perf_hooks'\nimport { chunkArray } from '@atproto/common'\nimport {\n type AtUriString,\n type DidString,\n type InferInput,\n type NsidString,\n type RecordSchema,\n type TypedLexMap,\n isTypedLexMap,\n lexParseJsonBytes,\n} from '@atproto/lex'\nimport { parseAtUriString } from '@atproto/syntax'\nimport type { DataPlaneClient } from '../data-plane/client/index.js'\nimport { site } from '../lexicons/index.js'\nimport { hydrationLogger } from '../logger.js'\nimport {\n type RecordLookupResult,\n RecordLookupStatus,\n} from '../proto/bsky_pb.js'\nimport {\n type ExternalHydrationOutcome,\n type ExternalHydrationRoot,\n type ExternalHydrationWork,\n type HydrationXrpcEndpoint,\n events,\n} from '../telemetry/events.js'\nimport { uriToDid } from '../util/uris.js'\nimport type {\n SiteStandardDocumentRecord,\n SiteStandardPublicationRecord,\n} from '../views/types.js'\nimport type { Actors } from './actor.js'\nimport type { Labels } from './label.js'\nimport {\n HydrationMap,\n type ItemRef,\n type RecordInfo,\n parseDate,\n safeTakedownRef,\n} from './util.js'\n\n/** Generic record bodies are decoded, but not validated against a Lexicon. */\nexport type ExternalRecord = RecordInfo<TypedLexMap>\nexport type ExternalRecords = HydrationMap<AtUriString, ExternalRecord>\n/** Keyed by `genericRecordKey({ uri, cid })` to retain versions of a URI. */\nexport type ExternalRecordsByRef = HydrationMap<ExactRecordKey, ExternalRecord>\nexport type ExternalRecordBacklinks = HydrationMap<AtUriString, AtUriString[]>\n\nexport type AtmosphereActivityItem = {\n uri: AtUriString\n sortedAt: Date\n}\n\nexport type AtmosphereTimeline = {\n items: AtmosphereActivityItem[]\n cursor?: string\n}\n\nexport type AtmosphereBacklinks = {\n backlinks: AtmosphereActivityItem[]\n cursor?: string\n}\n\nexport type ExternalRecordBacklinkCounts = HydrationMap<\n AtUriString,\n Partial<Record<NsidString, number>>\n>\n\nexport type AtmosphereBacklinksByActor = {\n backlinks: HydrationMap<AtUriString, AtUriString[]>\n truncated: boolean\n}\n\ndeclare const exactRecordKeySymbol: unique symbol\n\n/**\n * A version-exact `${uri}@${cid}` key, only produced by `genericRecordKey` for\n * a ref with a CID, so it can be parsed back by `parseGenericRecordKey`.\n */\nexport type ExactRecordKey = string & { [exactRecordKeySymbol]: true }\n\nexport type SiteStandardDocument = RecordInfo<SiteStandardDocumentRecord>\nexport type SiteStandardPublication = RecordInfo<SiteStandardPublicationRecord>\n\n/**\n * Keyed by `${uri}@${cid}` — see `genericRecordKey`. A single hydration\n * batch can pull more than one version of the same URI (different posts\n * pinning different cids), so the composite key is needed for O(1)\n * version-exact lookups.\n */\nexport type SiteStandardDocuments = HydrationMap<\n ExactRecordKey,\n SiteStandardDocument\n>\n/**\n * Keyed by `${uri}@${cid}`. See `SiteStandardDocuments` for the rationale.\n */\nexport type SiteStandardPublications = HydrationMap<\n ExactRecordKey,\n SiteStandardPublication\n>\nexport type SiteStandardRecords = {\n documents: SiteStandardDocuments\n publications: SiteStandardPublications\n}\n\nexport class ExternalHydrator {\n static readonly MAX_BACKLINK_PREVIEWS = 3\n static readonly MAX_BACKLINK_GALLERY_ITEMS = 10\n static readonly MAX_BACKLINK_FANOUT = 8\n /** Hard cap on generic batches per traversal, counting the root as pass 1. */\n static readonly MAX_EXTERNAL_HYDRATION_PASSES = 8\n /**\n * Entries per generic record RPC. The dataplane rejects larger URI lookups;\n * exact-ref lookups use the same bound defensively.\n */\n static readonly MAX_RECORDS_PER_REQUEST = 100\n\n constructor(public dataplane: DataPlaneClient) {}\n\n /**\n * Fetch exact record versions; unavailable records are represented by null.\n * Large inputs are fetched in sequential bounded requests; any failed request\n * rejects the whole call.\n */\n async getRecordsByRef(\n refs: Required<ItemRef>[],\n includeTakedowns = false,\n ): Promise<ExternalRecordsByRef> {\n const map: ExternalRecordsByRef = new HydrationMap()\n for (const chunk of chunkArray(\n refs,\n ExternalHydrator.MAX_RECORDS_PER_REQUEST,\n )) {\n const res = await this.dataplane.getRecordsByRef({ refs: chunk })\n // Results are positional within each request.\n for (let i = 0; i < chunk.length; i++) {\n map.set(\n genericRecordKey(chunk[i]),\n parseGenericRecord(res.results[i], includeTakedowns) ?? null,\n )\n }\n }\n return map\n }\n\n /**\n * Fetch the latest indexed versions without collection-specific validation.\n * Large inputs are fetched in sequential bounded requests; any failed request\n * rejects the whole call.\n */\n async getRecordsByURI(\n uris: AtUriString[],\n includeTakedowns = false,\n ): Promise<ExternalRecords> {\n const map: ExternalRecords = new HydrationMap()\n for (const chunk of chunkArray(\n uris,\n ExternalHydrator.MAX_RECORDS_PER_REQUEST,\n )) {\n const res = await this.dataplane.getRecordsByURI({ uris: chunk })\n // Results are positional within each request.\n for (let i = 0; i < chunk.length; i++) {\n map.set(\n chunk[i],\n parseGenericRecord(res.results[i], includeTakedowns) ?? null,\n )\n }\n }\n return map\n }\n\n /** Fetch a page of activity refs for subsequent generic record hydration. */\n async getAtmosphereTimeline(\n viewerDid: DidString,\n opts: { limit?: number; cursor?: string } = {},\n ): Promise<AtmosphereTimeline> {\n const res = await this.dataplane.getAtmosphereTimeline({\n viewerDid,\n ...opts,\n })\n return {\n items: res.items.map((item) => ({\n uri: item.uri as AtUriString,\n sortedAt: parseDate(item.sortedAt) ?? new Date(0),\n })),\n cursor: res.cursor || undefined,\n }\n }\n\n /** Fetch source-record counts, optionally restricted to one collection. */\n async getAtmosphereBacklinkCounts(\n targetUris: AtUriString[],\n collection?: NsidString,\n ): Promise<ExternalRecordBacklinkCounts> {\n const map: ExternalRecordBacklinkCounts = new HydrationMap()\n if (!targetUris.length) return map\n\n const res = await this.dataplane.getAtmosphereBacklinkCounts({\n targetUris,\n collection,\n })\n for (const result of res.results) {\n map.set(\n result.targetUri as AtUriString,\n Object.fromEntries(\n Object.entries(result.counts).map(([nsid, count]) => [\n nsid,\n Number(count),\n ]),\n ),\n )\n }\n return map\n }\n\n /** Fetch a page of source records linking to a target. */\n async getAtmosphereBacklinks(\n targetUri: AtUriString,\n collection: NsidString,\n opts: { limit?: number; cursor?: string } = {},\n ): Promise<AtmosphereBacklinks> {\n const res = await this.dataplane.getAtmosphereBacklinks({\n targetUri,\n collection,\n ...opts,\n })\n return {\n backlinks: res.backlinks.map((backlink) => ({\n uri: backlink.uri as AtUriString,\n sortedAt: parseDate(backlink.sortedAt) ?? new Date(0),\n })),\n cursor: res.cursor || undefined,\n }\n }\n\n /** Fetch an actor's links per target, retaining the batch truncation flag. */\n async getAtmosphereBacklinksByActor(\n targetUris: AtUriString[],\n actorDid: DidString,\n collection: NsidString,\n ): Promise<AtmosphereBacklinksByActor> {\n const backlinks: AtmosphereBacklinksByActor['backlinks'] =\n new HydrationMap()\n if (!targetUris.length) return { backlinks, truncated: false }\n\n const res = await this.dataplane.getAtmosphereBacklinksByActor({\n targetUris,\n actorDid,\n collection,\n })\n for (const result of res.results) {\n backlinks.set(\n result.targetUri as AtUriString,\n result.uris as AtUriString[],\n )\n }\n return { backlinks, truncated: res.truncated }\n }\n\n /**\n * Fetch exact `site.standard.{document,publication}` versions. Does NOT\n * resolve a document's publication; callers must pin it explicitly.\n */\n async getSiteStandardRecordsByRef(\n refs: ItemRef[],\n includeTakedowns = false,\n ): Promise<SiteStandardRecords> {\n const ssRefs = refs.filter(\n (ref): ref is Required<ItemRef> =>\n !!ref.cid && siteStandardKind(ref.uri) !== undefined,\n )\n const out = emptySiteStandardRecords()\n if (!ssRefs.length) return out\n\n const records = await this.getRecordsByRef(ssRefs, includeTakedowns)\n for (const [key, info] of records) {\n setSiteStandardRecord(out, parseGenericRecordKey(key).uri, key, info)\n }\n return out\n }\n}\n\n/**\n * Composes the lookup key for a ref: the bare URI for a latest-version lookup,\n * or `${uri}@${cid}` for an exact version. A single hydration batch can pull\n * several versions of one URI (different posts pinning different cids), so\n * exact maps need the composite for O(1) version-exact lookups. The two forms\n * never collide, so latest and exact lookups can share one traversal set.\n *\n * Throws on an empty CID rather than treating it as a latest lookup.\n */\nexport function genericRecordKey(ref: Required<ItemRef>): ExactRecordKey\nexport function genericRecordKey(ref: ItemRef): string\nexport function genericRecordKey({ uri, cid }: ItemRef): string {\n if (cid === undefined) return uri\n if (!cid) throw new Error(`Empty CID in record key for ${uri}`)\n return `${uri}@${cid}`\n}\n\n/**\n * Recover the URI and CID from an exact record key, splitting at the last `@`.\n * Throws if either part is missing. Components are trusted, not revalidated.\n */\nexport function parseGenericRecordKey(key: ExactRecordKey): Required<ItemRef> {\n const at = key.lastIndexOf('@')\n if (at <= 0 || at === key.length - 1) {\n throw new Error(`Malformed exact record key: ${key}`)\n }\n return {\n uri: key.slice(0, at) as AtUriString,\n cid: key.slice(at + 1),\n }\n}\n\n/**\n * Null generic external records (in both the URI-keyed and exact-ref maps,\n * keeping their keys) that are taken down by an actionable label, unless\n * takedowns are included, or whose owner is explicitly unavailable. Applies to\n * each record independently; counts and other records are unaffected.\n */\nexport function actionExternalRecordTakedowns(\n {\n externalRecords,\n externalRecordsByRef,\n actors,\n labels,\n }: {\n externalRecords?: ExternalRecords\n externalRecordsByRef?: ExternalRecordsByRef\n actors?: Actors\n labels?: Labels\n },\n includeTakedowns?: boolean,\n): void {\n // An absent actor entry was not hydrated; only `null` means unavailable.\n const isHidden = (uri: AtUriString) =>\n actors?.get(uriToDid(uri)) === null ||\n (!includeTakedowns && labels?.get(uri)?.isTakendown)\n for (const [uri, info] of externalRecords ?? []) {\n if (!info || !isHidden(uri)) continue\n externalRecords?.set(uri, null)\n externalRecordsByRef?.set(genericRecordKey({ uri, cid: info.cid }), null)\n }\n // Every exact version is checked on its own; versions are never collapsed.\n for (const [key, info] of externalRecordsByRef ?? []) {\n if (info && isHidden(parseGenericRecordKey(key).uri)) {\n externalRecordsByRef?.set(key, null)\n }\n }\n}\n\n/**\n * Generic lookup work of one external hydration traversal, shared by reference\n * through its nested passes. `capped` records hitting the pass limit; `partial`\n * records a recovered best-effort dependency failure.\n */\nexport type ExternalHydrationTracker = ExternalHydrationWork & {\n capped: boolean\n partial: boolean\n /**\n * Internal: set by the traversal's first dependency discovery, whose refs\n * are its roots.\n */\n rootsCounted: boolean\n}\n\n/**\n * Run `fn` with the caller's tracker, or as the owner of a new traversal when\n * there is none. Only the owner reports, once, including on failure, with the\n * context's XRPC endpoint (`'unknown'` if unset) and the traversal's wall time. A\n * returned traversal is `capped` if it hit the pass limit, else `partial` if\n * it recovered from a dependency failure. Owners that completed without\n * scheduling a batch did no work and are not reported; other outcomes always\n * are.\n */\nexport async function trackExternalHydration<T>(\n root: ExternalHydrationRoot,\n xrpcEndpoint: HydrationXrpcEndpoint | undefined,\n hydrationMetrics: ExternalHydrationTracker | undefined,\n fn: (hydrationMetrics: ExternalHydrationTracker) => Promise<T>,\n): Promise<T> {\n if (hydrationMetrics) return fn(hydrationMetrics)\n const owned: ExternalHydrationTracker = {\n recordLookups: 0,\n batches: 0,\n maxPass: 0,\n roots: 0,\n rootsResolved: 0,\n dependencies: 0,\n dependenciesResolved: 0,\n capped: false,\n partial: false,\n rootsCounted: false,\n }\n let outcome: ExternalHydrationOutcome = 'failed'\n const start = performance.now()\n try {\n const state = await fn(owned)\n outcome = owned.capped ? 'capped' : owned.partial ? 'partial' : 'completed'\n return state\n } finally {\n if (owned.batches || outcome !== 'completed') {\n events.externalHydrationTraversal({\n xrpcEndpoint: xrpcEndpoint ?? 'unknown',\n root,\n outcome,\n durationMs: performance.now() - start,\n recordLookups: owned.recordLookups,\n batches: owned.batches,\n maxPass: owned.maxPass,\n roots: owned.roots,\n rootsResolved: owned.rootsResolved,\n dependencies: owned.dependencies,\n dependenciesResolved: owned.dependenciesResolved,\n })\n }\n }\n}\n\nfunction parseGenericRecord(\n result: RecordLookupResult | undefined,\n includeTakedowns: boolean,\n): ExternalRecord | undefined {\n if (\n result?.status !== RecordLookupStatus.FOUND &&\n !(includeTakedowns && result?.status === RecordLookupStatus.TAKEN_DOWN)\n ) {\n return undefined\n }\n const entry = result.record\n if (!entry?.cid || !entry.record.byteLength) return undefined\n if (!includeTakedowns && entry.takenDown) return undefined\n\n let record: ReturnType<typeof lexParseJsonBytes>\n try {\n record = lexParseJsonBytes(entry.record, { strict: false })\n } catch {\n return undefined\n }\n if (!isTypedLexMap(record)) return undefined\n\n return {\n record,\n cid: entry.cid,\n sortedAt: parseDate(entry.sortedAt) ?? new Date(0),\n indexedAt: parseDate(entry.indexedAt) ?? new Date(0),\n takedownRef: safeTakedownRef(entry),\n }\n}\n\nconst emptySiteStandardRecords = (): SiteStandardRecords => ({\n documents: new HydrationMap(),\n publications: new HydrationMap(),\n})\n\nconst siteStandardKind = (\n uri: string,\n): keyof SiteStandardRecords | undefined => {\n const parsed = parseAtUriString(uri)\n if (!parsed.success) return undefined\n switch (parsed.value.collection) {\n case site.standard.document.$type:\n return 'documents'\n case site.standard.publication.$type:\n return 'publications'\n default:\n return undefined\n }\n}\n\n/**\n * Generic record bodies are unvalidated, so check them against the Lexicon\n * before exposing typed fields. Parse mode is not used, to preserve the\n * stored record as-is.\n */\nconst matchRecordInfo = <TSchema extends RecordSchema>(\n schema: TSchema,\n info: ExternalRecord | null | undefined,\n): RecordInfo<InferInput<TSchema>> | null =>\n info && schema.$matches(info.record, { strict: false })\n ? { ...info, record: info.record }\n : null\n\n/**\n * Store a generic record in the map matching its collection. Records failing\n * Lexicon validation are stored as null.\n */\nconst setSiteStandardRecord = (\n out: SiteStandardRecords,\n uri: string,\n key: ExactRecordKey,\n info: ExternalRecord | null | undefined,\n) => {\n switch (siteStandardKind(uri)) {\n case 'documents':\n out.documents.set(key, matchRecordInfo(site.standard.document.main, info))\n break\n case 'publications':\n out.publications.set(\n key,\n matchRecordInfo(site.standard.publication.main, info),\n )\n break\n }\n}\n\nexport type AssociatedSiteStandardRecord<T> = {\n ref: { uri: AtUriString; cid: string }\n info: T\n}\n\n/**\n * Read-path resolution: caller pinned a set of `(uri, cid)` strongRefs in\n * the post at write time, and we honor those exact versions. Any pair\n * that doesn't structurally agree is rejected, so downstream callers can\n * trust the returned shape.\n *\n * Pairing rules:\n * - Both slots referenced: `doc.site` must equal `publication.ref.uri`,\n * else both come back `undefined`.\n * - Doc with at-uri `site` referenced but no matching publication ref:\n * reject the whole pairing (doc claims a publication that should have\n * been pinned, but wasn't).\n * - Loose doc (web-URL `site`) referenced: publication stays `undefined`.\n * - Only a publication referenced: document stays `undefined`.\n *\n * Each returned slot carries the matching `ref` so callers can recover\n * the owner DID for blob-cdn URL building, etc.\n */\nexport const getSiteStandardRecordsFromHydrationMapsByRefs = (\n associatedRefs: readonly { uri: AtUriString; cid: string }[] | undefined,\n documents: SiteStandardDocuments | undefined,\n publications: SiteStandardPublications | undefined,\n): {\n document: AssociatedSiteStandardRecord<SiteStandardDocument> | undefined\n publication: AssociatedSiteStandardRecord<SiteStandardPublication> | undefined\n} => {\n if (!associatedRefs?.length) {\n return { document: undefined, publication: undefined }\n }\n\n // Resolve each ref against the hydration maps, taking the first hit on\n // each side.\n let document: AssociatedSiteStandardRecord<SiteStandardDocument> | undefined\n let publication:\n AssociatedSiteStandardRecord<SiteStandardPublication> | undefined\n for (const ref of associatedRefs) {\n const key = genericRecordKey(ref)\n if (!document) {\n const hit = documents?.get(key)\n if (hit) document = { ref, info: hit }\n }\n if (!publication) {\n const hit = publications?.get(key)\n if (hit) publication = { ref, info: hit }\n }\n if (document && publication) break\n }\n\n // Both refs resolved: enforce that the doc's `site` actually points at\n // the supplied publication. Mismatch means the post was misconstructed\n // (or tampered with), so reject the whole pairing.\n if (document && publication) {\n if (document.info.record.site !== publication.ref.uri) {\n hydrationLogger.warn(\n {\n documentUri: document.ref.uri,\n documentSite: document.info.record.site,\n publicationUri: publication.ref.uri,\n },\n 'site.standard byRefs lookup failed: doc.site does not match hydrated publication.uri',\n )\n return { document: undefined, publication: undefined }\n }\n }\n\n // Doc with at-uri `site` but no publication: the post should have\n // pinned the publication too. Treat as misconstructed.\n if (document && !publication) {\n const site = document.info.record.site\n if (site && site.startsWith('at://')) {\n hydrationLogger.warn(\n { documentUri: document.ref.uri, documentSite: site },\n 'site.standard byRefs lookup failed: document.site is AT URI but no matching publication was hydrated',\n )\n return { document: undefined, publication: undefined }\n }\n }\n\n return { document, publication }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"external.js","sourceRoot":"","sources":["../../src/hydration/external.ts"],"names":[],"mappings":"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","sourcesContent":["import { performance } from 'node:perf_hooks'\nimport { chunkArray } from '@atproto/common'\nimport {\n type AtUriString,\n type DidString,\n type InferInput,\n type NsidString,\n type RecordSchema,\n type TypedLexMap,\n isDidString,\n isTypedLexMap,\n lexParseJsonBytes,\n} from '@atproto/lex'\nimport { parseAtUriString } from '@atproto/syntax'\nimport type { DataPlaneClient } from '../data-plane/client/index.js'\nimport { site, social } from '../lexicons/index.js'\nimport {\n type RecordLookupResult,\n RecordLookupStatus,\n} from '../proto/bsky_pb.js'\nimport {\n type ExternalHydrationOutcome,\n type ExternalHydrationRoot,\n type ExternalHydrationWork,\n type HydrationXrpcEndpoint,\n events,\n} from '../telemetry/events.js'\nimport { recordUriOf, uriToDid } from '../util/uris.js'\nimport {\n type SiteStandardDocumentRecord,\n type SiteStandardPublicationRecord,\n isExternalEmbedType,\n isRecordWithMediaType,\n} from '../views/types.js'\nimport type { Actors } from './actor.js'\nimport type { Post, Posts } from './feed.js'\nimport type { Labels } from './label.js'\nimport {\n HydrationMap,\n type ItemRef,\n type RecordInfo,\n parseDate,\n safeTakedownRef,\n} from './util.js'\n\n/** Generic record bodies are decoded, but not validated against a Lexicon. */\nexport type ExternalRecord = RecordInfo<TypedLexMap>\nexport type ExternalRecords = HydrationMap<AtUriString, ExternalRecord>\n/** Keyed by `genericRecordKey({ uri, cid })` to retain versions of a URI. */\nexport type ExternalRecordsByRef = HydrationMap<ExactRecordKey, ExternalRecord>\nexport type ExternalRecordBacklinks = HydrationMap<AtUriString, AtUriString[]>\n\nexport type AtmosphereActivityItem = {\n uri: AtUriString\n sortedAt: Date\n}\n\nexport type AtmosphereTimeline = {\n items: AtmosphereActivityItem[]\n cursor?: string\n}\n\nexport type AtmosphereBacklinks = {\n backlinks: AtmosphereActivityItem[]\n cursor?: string\n}\n\nexport type ExternalRecordBacklinkCounts = HydrationMap<\n AtUriString,\n Partial<Record<NsidString, number>>\n>\n\nexport type AtmosphereBacklinksByActor = {\n backlinks: HydrationMap<AtUriString, AtUriString[]>\n truncated: boolean\n}\n\ndeclare const exactRecordKeySymbol: unique symbol\n\n/**\n * A version-exact `${uri}@${cid}` key, only produced by `genericRecordKey` for\n * a ref with a CID, so it can be parsed back by `parseGenericRecordKey`.\n */\nexport type ExactRecordKey = string & { [exactRecordKeySymbol]: true }\n\nexport type SiteStandardDocument = RecordInfo<SiteStandardDocumentRecord>\nexport type SiteStandardPublication = RecordInfo<SiteStandardPublicationRecord>\n\nexport type ExternalHydrationLevel = 'basic' | 'detailed'\n\n/**\n * Options for external hydration traversals. `hydration` picks one level for\n * the whole traversal (default `'detailed'`): `'basic'` hydrates renderable\n * content, moderation and aggregate backlink counts; `'detailed'` also samples\n * social-preview backlinks and hydrates their records and participants. The\n * remaining fields are traversal state shared by nested passes.\n */\nexport type ExternalHydrationOptions = {\n hydration?: ExternalHydrationLevel\n seenRecordKeys?: Set<string>\n hydrationPass?: number\n hydrationMetrics?: ExternalHydrationTracker\n}\n\n/**\n * Per-collection sample limits. Basic hydration skips social previews,\n * but keeps gallery items. Direct publication/photo references are singular.\n */\nexport const EXTERNAL_HYDRATION_LIMITS = {\n [site.standard.document.$type]: { recommendationPreviews: 3 },\n [site.standard.publication.$type]: { subscriberPreviews: 3 },\n [social.grain.gallery.$type]: { items: 10, favoritePreviews: 3 },\n} as const\n\n/** Maximum in-flight backlink sample RPCs, not a dependency-count limit. */\nexport const MAX_CONCURRENT_BACKLINK_REQUESTS = 8\n/** Maximum logical batches per traversal, counting the root as pass 1. */\nexport const MAX_EXTERNAL_HYDRATION_PASSES = 8\n\nexport class ExternalHydrator {\n /**\n * Entries per generic record RPC. The dataplane rejects larger URI lookups;\n * exact-ref lookups use the same bound defensively.\n */\n static readonly MAX_RECORDS_PER_REQUEST = 100\n /**\n * Target URIs per backlink-count RPC. The dataplane requires between 1 and\n * 100; this is a separate limit from `MAX_RECORDS_PER_REQUEST`.\n */\n static readonly MAX_BACKLINK_COUNT_TARGETS_PER_REQUEST = 100\n\n constructor(public dataplane: DataPlaneClient) {}\n\n /**\n * Fetch exact record versions; unavailable records are represented by null.\n * Large inputs are fetched in sequential bounded requests; any failed request\n * rejects the whole call.\n */\n async getRecordsByRef(\n refs: Required<ItemRef>[],\n includeTakedowns = false,\n ): Promise<ExternalRecordsByRef> {\n const map: ExternalRecordsByRef = new HydrationMap()\n for (const chunk of chunkArray(\n refs,\n ExternalHydrator.MAX_RECORDS_PER_REQUEST,\n )) {\n const res = await this.dataplane.getRecordsByRef({ refs: chunk })\n // Results are positional within each request.\n for (let i = 0; i < chunk.length; i++) {\n map.set(\n genericRecordKey(chunk[i]),\n parseGenericRecord(res.results[i], includeTakedowns) ?? null,\n )\n }\n }\n return map\n }\n\n /**\n * Fetch the latest indexed versions without collection-specific validation.\n * Large inputs are fetched in sequential bounded requests; any failed request\n * rejects the whole call.\n */\n async getRecordsByURI(\n uris: AtUriString[],\n includeTakedowns = false,\n ): Promise<ExternalRecords> {\n const map: ExternalRecords = new HydrationMap()\n for (const chunk of chunkArray(\n uris,\n ExternalHydrator.MAX_RECORDS_PER_REQUEST,\n )) {\n const res = await this.dataplane.getRecordsByURI({ uris: chunk })\n // Results are positional within each request.\n for (let i = 0; i < chunk.length; i++) {\n map.set(\n chunk[i],\n parseGenericRecord(res.results[i], includeTakedowns) ?? null,\n )\n }\n }\n return map\n }\n\n /** Fetch a page of activity refs for subsequent generic record hydration. */\n async getAtmosphereTimeline(\n viewerDid: DidString,\n opts: { limit?: number; cursor?: string } = {},\n ): Promise<AtmosphereTimeline> {\n const res = await this.dataplane.getAtmosphereTimeline({\n viewerDid,\n ...opts,\n })\n return {\n items: res.items.map((item) => ({\n uri: item.uri as AtUriString,\n sortedAt: parseDate(item.sortedAt) ?? new Date(0),\n })),\n cursor: res.cursor || undefined,\n }\n }\n\n /**\n * Fetch source-record counts, optionally restricted to one collection. Large\n * inputs are fetched in sequential bounded requests; any failed request\n * rejects the whole call.\n */\n async getAtmosphereBacklinkCounts(\n targetUris: AtUriString[],\n collection?: NsidString,\n ): Promise<ExternalRecordBacklinkCounts> {\n const map: ExternalRecordBacklinkCounts = new HydrationMap()\n for (const chunk of chunkArray(\n targetUris,\n ExternalHydrator.MAX_BACKLINK_COUNT_TARGETS_PER_REQUEST,\n )) {\n const res = await this.dataplane.getAtmosphereBacklinkCounts({\n targetUris: chunk,\n collection,\n })\n for (const result of res.results) {\n map.set(\n result.targetUri as AtUriString,\n Object.fromEntries(\n Object.entries(result.counts).map(([nsid, count]) => [\n nsid,\n Number(count),\n ]),\n ),\n )\n }\n }\n return map\n }\n\n /** Fetch a page of source records linking to a target. */\n async getAtmosphereBacklinks(\n targetUri: AtUriString,\n collection: NsidString,\n opts: { limit?: number; cursor?: string } = {},\n ): Promise<AtmosphereBacklinks> {\n const res = await this.dataplane.getAtmosphereBacklinks({\n targetUri,\n collection,\n ...opts,\n })\n return {\n backlinks: res.backlinks.map((backlink) => ({\n uri: backlink.uri as AtUriString,\n sortedAt: parseDate(backlink.sortedAt) ?? new Date(0),\n })),\n cursor: res.cursor || undefined,\n }\n }\n\n /** Fetch an actor's links per target, retaining the batch truncation flag. */\n async getAtmosphereBacklinksByActor(\n targetUris: AtUriString[],\n actorDid: DidString,\n collection: NsidString,\n ): Promise<AtmosphereBacklinksByActor> {\n const backlinks: AtmosphereBacklinksByActor['backlinks'] =\n new HydrationMap()\n if (!targetUris.length) return { backlinks, truncated: false }\n\n const res = await this.dataplane.getAtmosphereBacklinksByActor({\n targetUris,\n actorDid,\n collection,\n })\n for (const result of res.results) {\n backlinks.set(\n result.targetUri as AtUriString,\n result.uris as AtUriString[],\n )\n }\n return { backlinks, truncated: res.truncated }\n }\n}\n\n/**\n * Composes the lookup key for a ref: the bare URI for a latest-version lookup,\n * or `${uri}@${cid}` for an exact version. A single hydration batch can pull\n * several versions of one URI (different posts pinning different cids), so\n * exact maps need the composite for O(1) version-exact lookups. The two forms\n * never collide, so latest and exact lookups can share one traversal set.\n *\n * Throws on an empty CID rather than treating it as a latest lookup.\n */\nexport function genericRecordKey(ref: Required<ItemRef>): ExactRecordKey\nexport function genericRecordKey(ref: ItemRef): string\nexport function genericRecordKey({ uri, cid }: ItemRef): string {\n if (cid === undefined) return uri\n if (!cid) throw new Error(`Empty CID in record key for ${uri}`)\n return `${uri}@${cid}`\n}\n\n/**\n * Recover the URI and CID from an exact record key, splitting at the last `@`.\n * Throws if either part is missing. Components are trusted, not revalidated.\n */\nexport function parseGenericRecordKey(key: ExactRecordKey): Required<ItemRef> {\n const at = key.lastIndexOf('@')\n if (at <= 0 || at === key.length - 1) {\n throw new Error(`Malformed exact record key: ${key}`)\n }\n return {\n uri: key.slice(0, at) as AtUriString,\n cid: key.slice(at + 1),\n }\n}\n\n/**\n * Null generic external records (in both the URI-keyed and exact-ref maps,\n * keeping their keys) that are taken down by an actionable label, unless\n * takedowns are included, or whose owner is explicitly unavailable. Applies to\n * each record independently; counts and other records are unaffected.\n */\nexport function actionExternalRecordTakedowns(\n {\n externalRecords,\n externalRecordsByRef,\n actors,\n labels,\n }: {\n externalRecords?: ExternalRecords\n externalRecordsByRef?: ExternalRecordsByRef\n actors?: Actors\n labels?: Labels\n },\n includeTakedowns?: boolean,\n): void {\n // An absent actor entry was not hydrated; only `null` means unavailable.\n const isHidden = (uri: AtUriString) =>\n actors?.get(uriToDid(uri)) === null ||\n (!includeTakedowns && labels?.get(uri)?.isTakendown)\n for (const [uri, info] of externalRecords ?? []) {\n if (!info || !isHidden(uri)) continue\n externalRecords?.set(uri, null)\n externalRecordsByRef?.set(genericRecordKey({ uri, cid: info.cid }), null)\n }\n // Every exact version is checked on its own; versions are never collapsed.\n for (const [key, info] of externalRecordsByRef ?? []) {\n if (info && isHidden(parseGenericRecordKey(key).uri)) {\n externalRecordsByRef?.set(key, null)\n }\n }\n}\n\n/**\n * Generic lookup work of one external hydration traversal, shared by reference\n * through its nested passes. `capped` records hitting the pass limit; `partial`\n * records a recovered best-effort dependency failure.\n */\nexport type ExternalHydrationTracker = ExternalHydrationWork & {\n capped: boolean\n partial: boolean\n /**\n * Internal: set by the traversal's first dependency discovery, whose refs\n * are its roots.\n */\n rootsCounted: boolean\n}\n\n/**\n * Run `fn` with the caller's tracker, or as the owner of a new traversal when\n * there is none. Only the owner reports, once, including on failure, with the\n * context's XRPC endpoint (`'unknown'` if unset) and the traversal's wall time. A\n * returned traversal is `capped` if it hit the pass limit, else `partial` if\n * it recovered from a dependency failure. Owners that completed report when\n * they scheduled a batch or inspected roots (even prehydrated or unavailable\n * ones); only genuinely empty successful calls are not reported. Other\n * outcomes always are.\n */\nexport async function trackExternalHydration<T>(\n root: ExternalHydrationRoot,\n xrpcEndpoint: HydrationXrpcEndpoint | undefined,\n hydrationMetrics: ExternalHydrationTracker | undefined,\n fn: (hydrationMetrics: ExternalHydrationTracker) => Promise<T>,\n): Promise<T> {\n if (hydrationMetrics) return fn(hydrationMetrics)\n const owned: ExternalHydrationTracker = {\n recordLookups: 0,\n batches: 0,\n maxPass: 0,\n roots: 0,\n rootsResolved: 0,\n dependencies: 0,\n dependenciesResolved: 0,\n capped: false,\n partial: false,\n rootsCounted: false,\n }\n let outcome: ExternalHydrationOutcome = 'failed'\n const start = performance.now()\n try {\n const state = await fn(owned)\n outcome = owned.capped ? 'capped' : owned.partial ? 'partial' : 'completed'\n return state\n } finally {\n if (owned.batches > 0 || owned.roots > 0 || outcome !== 'completed') {\n events.externalHydrationTraversal({\n xrpcEndpoint: xrpcEndpoint ?? 'unknown',\n root,\n outcome,\n durationMs: performance.now() - start,\n recordLookups: owned.recordLookups,\n batches: owned.batches,\n maxPass: owned.maxPass,\n roots: owned.roots,\n rootsResolved: owned.rootsResolved,\n dependencies: owned.dependencies,\n dependenciesResolved: owned.dependenciesResolved,\n })\n }\n }\n}\n\nfunction parseGenericRecord(\n result: RecordLookupResult | undefined,\n includeTakedowns: boolean,\n): ExternalRecord | undefined {\n if (\n result?.status !== RecordLookupStatus.FOUND &&\n !(includeTakedowns && result?.status === RecordLookupStatus.TAKEN_DOWN)\n ) {\n return undefined\n }\n const entry = result.record\n if (!entry?.cid || !entry.record.byteLength) return undefined\n if (!includeTakedowns && entry.takenDown) return undefined\n\n let record: ReturnType<typeof lexParseJsonBytes>\n try {\n record = lexParseJsonBytes(entry.record, { strict: false })\n } catch {\n return undefined\n }\n if (!isTypedLexMap(record)) return undefined\n\n return {\n record,\n cid: entry.cid,\n sortedAt: parseDate(entry.sortedAt) ?? new Date(0),\n indexedAt: parseDate(entry.indexedAt) ?? new Date(0),\n takedownRef: safeTakedownRef(entry),\n }\n}\n\nconst siteStandardKind = (\n uri: string,\n): 'documents' | 'publications' | undefined => {\n const parsed = parseAtUriString(uri)\n if (!parsed.success) return undefined\n switch (parsed.value.collection) {\n case site.standard.document.$type:\n return 'documents'\n case site.standard.publication.$type:\n return 'publications'\n default:\n return undefined\n }\n}\n\n/**\n * Generic record bodies are unvalidated, so check them against the Lexicon\n * before exposing typed fields. Parse mode is not used, to preserve the\n * stored record as-is.\n */\nconst matchRecordInfo = <TSchema extends RecordSchema>(\n schema: TSchema,\n info: ExternalRecord | null | undefined,\n): RecordInfo<InferInput<TSchema>> | null =>\n info && schema.$matches(info.record, { strict: false })\n ? { ...info, record: info.record }\n : null\n\nexport type AssociatedSiteStandardRecord<T> = {\n ref: { uri: AtUriString; cid: string }\n info: T\n}\n\n/**\n * Select one pinned embed root. Legacy publication-first pairs may put their\n * document second; no other authored refs participate in hydration.\n */\nexport function getExternalRootRef(\n refs: readonly Required<ItemRef>[] | undefined,\n): Required<ItemRef> | undefined {\n let root = refs?.[0]\n if (!root) return undefined\n if (siteStandardKind(root.uri) === 'publications') {\n const second = refs?.[1]\n if (second && siteStandardKind(second.uri) === 'documents') root = second\n }\n return isHydratableAssociatedRef(root) ? root : undefined\n}\n\n/** Select a pinned Standard.site root and its derived publication, if needed. */\nexport function getSiteStandardRecordsFromRefs(\n associatedRefs: readonly Required<ItemRef>[] | undefined,\n records: ExternalRecordsByRef | undefined,\n dependencies?: ExternalRecords,\n): {\n document: AssociatedSiteStandardRecord<SiteStandardDocument> | undefined\n publication: AssociatedSiteStandardRecord<SiteStandardPublication> | undefined\n} {\n const empty = { document: undefined, publication: undefined }\n const ref = getExternalRootRef(associatedRefs)\n if (!ref) return empty\n const info = records?.get(genericRecordKey(ref))\n if (siteStandardKind(ref.uri) === 'publications') {\n const publication = matchRecordInfo(site.standard.publication.main, info)\n return publication\n ? { document: undefined, publication: { ref, info: publication } }\n : empty\n }\n if (siteStandardKind(ref.uri) !== 'documents') return empty\n const document = matchRecordInfo(site.standard.document.main, info)\n if (!document) return empty\n const documentSelection = { ref, info: document }\n const docSite = document.record.site\n if (!docSite.startsWith('at://')) {\n return { document: documentSelection, publication: undefined }\n }\n const uri = recordUriOf(docSite, site.standard.publication.$type)\n if (!uri) return empty\n const publication = matchRecordInfo(\n site.standard.publication.main,\n dependencies?.get(uri),\n )\n if (!publication) return empty\n return {\n document: documentSelection,\n publication: { ref: { uri, cid: publication.cid }, info: publication },\n }\n}\n\nconst externalAssociatedRefs = (\n post: Post['record'],\n): { uri: AtUriString; cid: string }[] => {\n const embed = post?.embed\n if (!embed) return []\n if (isExternalEmbedType(embed)) {\n return embed.external.associatedRefs ?? []\n }\n if (isRecordWithMediaType(embed) && isExternalEmbedType(embed.media)) {\n return embed.media.external.associatedRefs ?? []\n }\n return []\n}\n\n/**\n * An exact ref needs a nonempty CID and a complete record URI in a DID repo,\n * so malformed refs are skipped instead of becoming latest lookups.\n */\nconst isHydratableAssociatedRef = (ref: {\n uri: string\n cid: string\n}): boolean => {\n if (!ref.cid) return false\n const parsed = parseAtUriString(ref.uri)\n if (!parsed.success) return false\n const { authority, collection, rkey, query, hash } = parsed.value\n return (\n isDidString(authority) &&\n !!collection &&\n !!rkey &&\n query === undefined &&\n hash === undefined\n )\n}\n\n/** Collect one root per post across all layers, deduped by exact ref. */\nexport const externalAssociatedRefsFromPosts = (\n ...postLayers: Posts[]\n): Required<ItemRef>[] => {\n const seen = new Set<string>()\n const out: Required<ItemRef>[] = []\n for (const layer of postLayers) {\n for (const item of layer.values()) {\n if (!item) continue\n const ref = getExternalRootRef(externalAssociatedRefs(item.record))\n if (!ref) continue\n const key = genericRecordKey(ref)\n if (seen.has(key)) continue\n seen.add(key)\n out.push({ uri: ref.uri, cid: ref.cid })\n }\n }\n return out\n}\n"]}
|
|
@@ -7,7 +7,7 @@ import type { BookmarkInfo, Notification } from '../proto/bsky_pb.js';
|
|
|
7
7
|
import { type HydrationXrpcEndpoint } from '../telemetry/events.js';
|
|
8
8
|
import type { ParsedLabelers } from '../util.js';
|
|
9
9
|
import { type ActivitySubscriptionStates, ActorHydrator, type Actors, type KnownFollowersStates, type KnownLikersStates, type ProfileAggs, type ProfileLinks, type ProfileViewerStates } from './actor.js';
|
|
10
|
-
import { type
|
|
10
|
+
import { type ExternalHydrationOptions, ExternalHydrator, type ExternalRecordBacklinkCounts, type ExternalRecordBacklinks, type ExternalRecords, type ExternalRecordsByRef } from './external.js';
|
|
11
11
|
import { type FeedGenAggs, type FeedGenViewerStates, type FeedGens, FeedHydrator, type FeedItem, type GetPostsHydrationOptions, type Likes, type Post, type PostAggs, type PostViewerStates, type Postgates, type Posts, type Reposts, type ThreadContexts, type Threadgates } from './feed.js';
|
|
12
12
|
import { type Follows, GraphHydrator, type ListAggs, type ListItems, type ListMembershipStates, type ListViewerStates, type Lists, type StarterPackAggs, type StarterPacks, type Verifications } from './graph.js';
|
|
13
13
|
import { LabelHydrator, type LabelerAggs, type LabelerViewerStates, type Labelers, Labels } from './label.js';
|
|
@@ -87,8 +87,6 @@ export type HydrationState = {
|
|
|
87
87
|
/** Available backlink source URIs per target, potentially from a partial page. */
|
|
88
88
|
externalRecordBacklinks?: ExternalRecordBacklinks;
|
|
89
89
|
externalRecordBacklinkCounts?: ExternalRecordBacklinkCounts;
|
|
90
|
-
siteStandardDocuments?: SiteStandardDocuments;
|
|
91
|
-
siteStandardPublications?: SiteStandardPublications;
|
|
92
90
|
};
|
|
93
91
|
type HydrateKnownLikersOptions = {
|
|
94
92
|
subjectUris: AtUriString[];
|
|
@@ -171,15 +169,16 @@ export declare class Hydrator {
|
|
|
171
169
|
* generic batch within the traversal (the root batch is pass 1); nested
|
|
172
170
|
* batches continue the count. Nested passes share the caller's
|
|
173
171
|
* `hydrationMetrics` tracker; without one, this call owns the traversal and
|
|
174
|
-
* reports its totals.
|
|
172
|
+
* reports its totals. `options.hydration` (default `'detailed'`) applies to
|
|
173
|
+
* the whole traversal and is propagated to every nested pass.
|
|
175
174
|
*/
|
|
176
|
-
hydrateEmbedExternalViewFromUris(uris: AtUriString[], ctx: HydrateCtx,
|
|
175
|
+
hydrateEmbedExternalViewFromUris(uris: AtUriString[], ctx: HydrateCtx, options?: ExternalHydrationOptions): Promise<HydrationState>;
|
|
177
176
|
/**
|
|
178
177
|
* Like `hydrateEmbedExternalViewFromUris`, but for exact record versions
|
|
179
178
|
* pinned by strong refs. Roots land only in `externalRecordsByRef`; their
|
|
180
179
|
* URI dependencies are hydrated as latest versions by the URI path.
|
|
181
180
|
*/
|
|
182
|
-
hydrateEmbedExternalViewFromRefs(refs: Required<ItemRef>[], ctx: HydrateCtx,
|
|
181
|
+
hydrateEmbedExternalViewFromRefs(refs: Required<ItemRef>[], ctx: HydrateCtx, options?: ExternalHydrationOptions): Promise<HydrationState>;
|
|
183
182
|
private hydrateEmbedExternalView;
|
|
184
183
|
/**
|
|
185
184
|
* Discover view dependencies by URI collection and hydrate new URIs in a batch.
|
|
@@ -194,9 +193,12 @@ export declare class Hydrator {
|
|
|
194
193
|
* `hydrationPass` is the number of the generic batch that produced `state`
|
|
195
194
|
* (1-based, default 1 for prehydrated state, which is treated as the root
|
|
196
195
|
* batch); the next nested batch is `hydrationPass + 1`. `hydrationMetrics`
|
|
197
|
-
* is shared as in `hydrateEmbedExternalViewFromUris`.
|
|
196
|
+
* is shared as in `hydrateEmbedExternalViewFromUris`. `options.hydration`
|
|
197
|
+
* (default `'detailed'`) decides, per collection, whether social-preview
|
|
198
|
+
* samples are scheduled; backlink counts and content dependencies are
|
|
199
|
+
* discovered at both levels.
|
|
198
200
|
*/
|
|
199
|
-
hydrateExternalViewDependencies(state: HydrationState, refs: ItemRef[], ctx: HydrateCtx,
|
|
201
|
+
hydrateExternalViewDependencies(state: HydrationState, refs: ItemRef[], ctx: HydrateCtx, options?: ExternalHydrationOptions): Promise<HydrationState>;
|
|
200
202
|
private discoverExternalViewDependencies;
|
|
201
203
|
hydrateThreadPosts(refs: ItemRef[], ctx: HydrateCtx, options?: Pick<HydratePostsOptions, 'knownLikers'>): Promise<HydrationState>;
|
|
202
204
|
private hydrateKnownLikers;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hydrator.d.ts","sourceRoot":"","sources":["../../src/hydration/hydrator.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,SAAS,EAGf,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAA;AACxF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AACpE,OAAO,KAAK,EACV,kBAAkB,EAClB,wBAAwB,EACzB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,GAAG,EAAkC,MAAM,sBAAsB,CAAA;AAE1E,OAAO,KAAK,EAEV,YAAY,EACZ,YAAY,EAEb,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,KAAK,qBAAqB,EAAU,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"hydrator.d.ts","sourceRoot":"","sources":["../../src/hydration/hydrator.ts"],"names":[],"mappings":"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"}
|