useDocHandle.ts
1 import { 2 parseAutomergeUrl, 3 type AutomergeUrl, 4 type DocHandle, 5 type HandleState, 6 } from "@automerge/automerge-repo/slim" 7 import {createEffect, createResource, useContext} from "solid-js" 8 import {access, type MaybeAccessor} from "@solid-primitives/utils" 9 import {RepoContext} from "./context.js" 10 import type {UseDocHandleOptions} from "./types.js" 11 12 const readyStates = ["ready", "deleted", "unavailable"] as HandleState[] 13 const badStates = ["deleted", "unavailable"] as HandleState[] 14 15 /** 16 * get a 17 * [DocHandle](https://automerge.org/automerge-repo/classes/_automerge_automerge_repo.DocHandle.html) 18 * from an 19 * [AutomergeUrl](https://automerge.org/automerge-repo/types/_automerge_automerge_repo.AutomergeUrl.html) 20 * as a 21 * [Resource](https://docs.solidjs.com/reference/basic-reactivity/create-resource). 22 * Waits for the handle to be 23 * [ready](https://automerge.org/automerge-repo/variables/_automerge_automerge_repo.HandleState-1.html). 24 */ 25 export default function useDocHandle<T>( 26 url: MaybeAccessor<AutomergeUrl | undefined>, 27 options?: UseDocHandleOptions 28 ) { 29 const contextRepo = useContext(RepoContext) 30 31 if (!options?.repo && !contextRepo) { 32 throw new Error("use outside <RepoContext> requires options.repo") 33 } 34 35 const repo = (options?.repo || contextRepo)! 36 37 function getExistingHandle() { 38 if (options?.["~skipInitialValue"]) return undefined 39 const unwrappedURL = access(url) 40 if (!unwrappedURL) return undefined 41 const parsedURL = parseAutomergeUrl(unwrappedURL) 42 const existingHandle = repo.handles[parsedURL.documentId] 43 if (existingHandle?.isReady()) { 44 return existingHandle as DocHandle<T> 45 } 46 } 47 48 const [handle, {mutate}] = createResource( 49 url, 50 async url => { 51 const handle = await repo.find<T>(url, { 52 allowableStates: readyStates, 53 }) 54 const reject = (state: HandleState) => 55 Promise.reject(new Error(`document not available: [${state}]`)) 56 57 if (handle.isReady()) { 58 return handle 59 } else if (handle.inState(badStates)) { 60 return reject(handle.state) 61 } 62 63 return handle.whenReady(readyStates).then(() => { 64 if (handle.isReady()) { 65 return handle 66 } 67 return reject(handle.state) 68 }) 69 }, 70 { 71 initialValue: getExistingHandle(), 72 } 73 ) 74 75 createEffect(() => { 76 if (!access(url)) { 77 mutate() 78 } 79 }) 80 81 return handle 82 }