a tool for shared writing and social publishing
at main 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