makeDocumentProjection.ts
1 import {onCleanup} from "solid-js" 2 import {Doc, DocHandle, DocHandleChangePayload} from "@automerge/automerge-repo" 3 import autoproduce from "./autoproduce.js" 4 import {createStore, produce, reconcile, type Store} from "solid-js/store" 5 6 const cache = new WeakMap< 7 DocHandle<unknown>, 8 { 9 refs: number 10 store: Store<Doc<unknown>> 11 cleanup(): void 12 } 13 >() 14 15 /** 16 * make a fine-grained live view of a document from its handle. 17 * @param handle an Automerge 18 * [DocHandle](https://automerge.org/automerge-repo/classes/_automerge_automerge_repo.DocHandle.html) 19 */ 20 export default function makeDocumentProjection<T>(handle: DocHandle<T>) { 21 onCleanup(() => { 22 const item = cache.get(handle)! 23 if (!item) return 24 if (!item.refs--) { 25 item.cleanup() 26 } 27 }) 28 29 if (cache.has(handle)) { 30 const item = cache.get(handle)! 31 item.refs++ 32 return item.store as Doc<T> 33 } 34 35 const [doc, set] = createStore<Doc<T>>(handle.doc()) 36 37 cache.set(handle, { 38 refs: 0, 39 store: doc, 40 cleanup() { 41 handle.off("change", patch) 42 handle.off("delete", ondelete) 43 }, 44 }) 45 46 function patch(payload: DocHandleChangePayload<T>) { 47 set(produce(autoproduce(payload.patches))) 48 } 49 50 function ondelete() { 51 set(reconcile({} as Doc<T>)) 52 } 53 54 handle.on("change", patch) 55 handle.on("delete", ondelete) 56 57 handle.whenReady().then(() => { 58 set(handle.doc()) 59 }) 60 61 return doc 62 }