AppView in a box as a Vite plugin thing
hatk.dev
1# BaseContext: Shared Hydration Context
2
3**Goal:** Extract a shared `BaseContext` type from hatk so hydrators work with both feed and XRPC contexts without manual field spreading or `as any` casts.
4
5**Problem:** `HydrateContext` and `XrpcContext` share 90% of their fields (db, lookup, count, getRecords, labels, blobUrl, viewer). Hydrators that need to work with both must manually spread every field and cast. Adding `getRecords` to `XrpcContext` required touching four construction sites.
6
7## Design
8
9### BaseContext
10
11Shared interface in `hydrate.ts`. Provides data access tools any hydrator needs.
12
13```ts
14export interface BaseContext {
15 viewer: { did: string; handle?: string } | null
16 db: { query: (sql: string, params?: unknown[]) => Promise<unknown[]> }
17 getRecords: <R = unknown>(collection: string, uris: string[]) => Promise<Map<string, Row<R>>>
18 lookup: <R = unknown>(collection: string, field: string, values: string[]) => Promise<Map<string, Row<R>>>
19 count: (collection: string, field: string, values: string[]) => Promise<Map<string, number>>
20 labels: (uris: string[]) => Promise<Map<string, unknown[]>>
21 blobUrl: (did: string, ref: unknown, preset?: 'avatar' | 'banner' | 'feed_thumbnail' | 'feed_fullsize') => string | undefined
22}
23```
24
25`buildBaseContext(viewer)` constructs one. No items parameter — items are always a separate argument.
26
27### XrpcContext extends BaseContext
28
29Adds XRPC-specific fields: params, cursor, limit, search, resolve, exists, packCursor, unpackCursor, isTakendown, filterTakendownDids, db.run. Context construction in xrpc.ts and opengraph.ts reuses `buildBaseContext` and extends.
30
31### Feed hydrate signature
32
33Changes from `(ctx: HydrateContext<T>) => Promise<View[]>` to `(ctx: BaseContext, items: Row<T>[]) => Promise<View[]>`. `HydrateContext` is removed entirely.
34
35### Hydrator reuse
36
37XRPC handlers pass `ctx` directly to hydrators since `XrpcContext extends BaseContext`:
38
39```ts
40// Before
41const galleries = await hydrateGalleries({
42 items: result.records, viewer: ctx.viewer, db: ctx.db,
43 getRecords: ctx.getRecords, lookup: ctx.lookup, count: ctx.count,
44 labels: ctx.labels, blobUrl: ctx.blobUrl,
45} as any);
46
47// After
48const galleries = await hydrateGalleries(ctx, result.records);
49```
50
51## Files to change
52
53**hatk framework:**
54- `hydrate.ts` — Add BaseContext, remove HydrateContext, buildBaseContext
55- `xrpc.ts` — XrpcContext extends BaseContext, reuse buildBaseContext
56- `opengraph.ts` — Reuse buildBaseContext
57- `feeds.ts` — Hydrate signature, executeFeed, exports
58- `cli.ts` — Codegen: BaseContext imports/exports, defineFeed overloads
59
60**Template projects:**
61- `server/feeds/_hydrate.ts` — Accept (ctx: BaseContext, items: Row<T>[])
62- `server/feeds/*.ts` — Pass (ctx, items) to hydrate
63- `server/xrpc/*.ts` — Pass ctx directly to hydrators