/ src / useDocHandle.ts
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  }