AppView in a box as a Vite plugin thing hatk.dev
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add getRecordsMap shared helper and getRecords to XrpcContext

Extract duplicated getRecords logic (fetch by URIs, reshape, return Map)
into a generic getRecordsMap function in db.ts, used by both
HydrateContext and XrpcContext. Adds getRecords to the XrpcContext
interface so XRPC handlers can fetch shaped records by URI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+23 -12
+12
packages/hatk/src/database/db.ts
··· 1201 1201 return uris.map((u) => byUri.get(u)).filter(Boolean) 1202 1202 } 1203 1203 1204 + /** Fetch records by URIs and return as a shaped Map keyed by URI. */ 1205 + export async function getRecordsMap<R = unknown>(collection: string, uris: string[]): Promise<Map<string, Row<R>>> { 1206 + if (uris.length === 0) return new Map() 1207 + const records = await getRecordsByUris(collection, uris) 1208 + const map = new Map<string, Row<R>>() 1209 + for (const r of records) { 1210 + const shaped = reshapeRow(r, r?.__childData, r?.__unionData) 1211 + if (shaped) map.set(shaped.uri, shaped as Row<R>) 1212 + } 1213 + return map 1214 + } 1215 + 1204 1216 /** 1205 1217 * Multi-phase search across any collection's records. 1206 1218 *
+4 -12
packages/hatk/src/hydrate.ts
··· 1 1 import { 2 - getRecordsByUris, 2 + getRecordsMap, 3 3 countByFieldBatch, 4 4 lookupByFieldBatch, 5 5 querySQL, 6 - reshapeRow, 7 6 queryLabelsForUris, 8 7 filterTakendownDids, 8 + getRecordsByUris, 9 + reshapeRow, 9 10 } from './database/db.ts' 10 11 import { blobUrl } from './xrpc.ts' 11 12 import type { Row } from './lex-types.ts' ··· 78 79 items, 79 80 viewer, 80 81 db: { query: querySQL }, 81 - getRecords: async (collection, uris) => { 82 - if (uris.length === 0) return new Map() 83 - const records = await getRecordsByUris(collection, uris) 84 - const map = new Map<string, Row<unknown>>() 85 - for (const r of records) { 86 - const shaped = reshapeRow(r, r?.__childData, r?.__unionData) 87 - if (shaped) map.set(shaped.uri, shaped) 88 - } 89 - return map as any 90 - }, 82 + getRecords: getRecordsMap, 91 83 lookup: async (collection, field, values) => { 92 84 if (values.length === 0) return new Map() 93 85 const unique = [...new Set(values.filter(Boolean))]
+3
packages/hatk/src/opengraph.ts
··· 26 26 lookupByFieldBatch, 27 27 countByFieldBatch, 28 28 queryLabelsForUris, 29 + getRecordsMap, 29 30 } from './database/db.ts' 30 31 import { resolveRecords } from './hydrate.ts' 31 32 import { blobUrl } from './xrpc.ts' ··· 143 144 filterTakendownDids, 144 145 search: searchRecords, 145 146 resolve: resolveRecords as any, 147 + getRecords: getRecordsMap, 146 148 lookup: async (collection, field, values) => { 147 149 if (values.length === 0) return new Map() 148 150 const unique = [...new Set(values.filter(Boolean))] ··· 229 231 filterTakendownDids, 230 232 search: searchRecords, 231 233 resolve: resolveRecords as any, 234 + getRecords: getRecordsMap, 232 235 lookup: async (collection, field, values) => { 233 236 if (values.length === 0) return new Map() 234 237 const unique = [...new Set(values.filter(Boolean))]
+4
packages/hatk/src/xrpc.ts
··· 33 33 lookupByFieldBatch, 34 34 countByFieldBatch, 35 35 queryLabelsForUris, 36 + getRecordsMap, 36 37 } from './database/db.ts' 37 38 import { resolveRecords } from './hydrate.ts' 38 39 import { getLexicon } from './database/schema.ts' ··· 90 91 opts?: { limit?: number; cursor?: string; fuzzy?: boolean }, 91 92 ) => Promise<{ records: Row<Records[K]>[]; cursor?: string }> 92 93 resolve: <R = unknown>(uris: string[]) => Promise<Row<R>[]> 94 + getRecords: <R = unknown>(collection: string, uris: string[]) => Promise<Map<string, Row<R>>> 93 95 lookup: <R = any>(collection: string, field: string, values: string[]) => Promise<Map<string, Row<R>>> 94 96 count: (collection: string, field: string, values: string[]) => Promise<Map<string, number>> 95 97 exists: (collection: string, filters: Record<string, string>) => Promise<boolean> ··· 208 210 filterTakendownDids, 209 211 search: searchRecords, 210 212 resolve: resolveRecords as any, 213 + getRecords: getRecordsMap, 211 214 lookup: async (collection, field, values) => { 212 215 if (values.length === 0) return new Map() 213 216 const unique = [...new Set(values.filter(Boolean))] ··· 270 273 filterTakendownDids, 271 274 search: searchRecords, 272 275 resolve: resolveRecords as any, 276 + getRecords: getRecordsMap, 273 277 lookup: async (collection, field, values) => { 274 278 if (values.length === 0) return new Map() 275 279 const unique = [...new Set(values.filter(Boolean))]