a tool for shared writing and social publishing
298
fork

Configure Feed

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

at 1bac2981117cfcb590551002fdff02c126b05f2e 89 lines 2.9 kB view raw
1import { PostgresJsDatabase } from "drizzle-orm/postgres-js"; 2import * as driz from "drizzle-orm"; 3import type { Fact } from "."; 4import { replicache_clients } from "drizzle/schema"; 5import type { Attribute, FilterAttributes } from "./attributes"; 6import { ReadTransaction, WriteTransaction } from "replicache"; 7import { PgTransaction } from "drizzle-orm/pg-core"; 8 9export function FactWithIndexes(f: Fact<Attribute>) { 10 let indexes: { 11 eav: string; 12 vae?: string; 13 } = { 14 eav: `${f.entity}-${f.attribute}-${f.id}`, 15 }; 16 if ( 17 f.data.type === "reference" || 18 f.data.type === "ordered-reference" || 19 f.data.type === "spatial-reference" 20 ) 21 indexes.vae = `${f.data.value}-${f.attribute}`; 22 return { ...f, indexes }; 23} 24 25export async function getClientGroup( 26 db: PgTransaction<any, any, any>, 27 clientGroupID: string, 28): Promise<{ [clientID: string]: number }> { 29 let data = await db 30 .select() 31 .from(replicache_clients) 32 .where(driz.eq(replicache_clients.client_group, clientGroupID)); 33 if (!data) return {}; 34 return data.reduce( 35 (acc, clientRecord) => { 36 acc[clientRecord.client_id] = clientRecord.last_mutation; 37 return acc; 38 }, 39 {} as { [clientID: string]: number }, 40 ); 41} 42 43export const scanIndex = (tx: ReadTransaction) => ({ 44 async eav<A extends Attribute>(entity: string, attribute: A | "") { 45 return ( 46 ( 47 await tx 48 .scan<Fact<A>>({ indexName: "eav", prefix: `${entity}-${attribute}` }) 49 // Hack rn because of the rich bluesky-post type 50 .toArray() 51 ).filter((f) => attribute === "" || f.attribute === attribute) 52 ); 53 }, 54 async vae< 55 A extends keyof FilterAttributes<{ 56 type: "reference" | "ordered-reference" | "spatial-reference"; 57 }>, 58 >(entity: string, attribute: A) { 59 return ( 60 await tx 61 .scan<Fact<A>>({ indexName: "vae", prefix: `${entity}-${attribute}` }) 62 .toArray() 63 ).filter((f) => f.attribute === attribute); 64 }, 65}); 66 67export const scanIndexLocal = (initialFacts: Fact<any>[]) => ({ 68 eav<A extends Attribute>(entity: string, attribute: A) { 69 return initialFacts.filter( 70 (f) => f.entity === entity && f.attribute === attribute, 71 ) as SafeArray<Fact<A>>; 72 }, 73}); 74 75// Base utility type for making types compatible with ReadonlyJSONObject 76export type AsReadonlyJSONObject<T> = T & { 77 [key: string]: undefined; 78}; 79 80// Recursive utility type for nested objects 81export type DeepAsReadonlyJSONValue<T> = T extends object 82 ? T extends Array<infer U> 83 ? ReadonlyArray<DeepAsReadonlyJSONValue<U>> // Handle arrays 84 : AsReadonlyJSONObject<{ 85 [K in keyof T]: DeepAsReadonlyJSONValue<T[K]>; 86 }> 87 : T extends string | number | boolean | null 88 ? T // Primitive types that already match ReadonlyJSONValue 89 : never; // For other types that can't be converted