Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/danielroe/zero-vue. Vue bindings for Zero
sync vue zero
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211// based on https://github.com/rocicorp/mono/tree/main/packages/zero-solid
import type { AnyViewFactory, Change, Entry, ErroredQuery, Format, Input, Node, Output, Query, QueryErrorDetails, QueryResultDetails, Schema, Stream, TTL,} from '@rocicorp/zero'import type { ViewChange } from '@rocicorp/zero/bindings'import type { Ref, ShallowRef } from 'vue'import { applyChange, skipYields } from '@rocicorp/zero/bindings'import { ref, shallowRef } from 'vue'
export type QueryStatus = QueryResultDetails['type']export type QueryError = QueryErrorDetails['error']
export class VueView implements Output { readonly #input: Input readonly #format: Format readonly #onDestroy: () => void readonly #updateTTL: (ttl: TTL) => void
#root: Entry #data: ShallowRef<Entry> #status: Ref<QueryStatus> #error: Ref<QueryError | undefined> #isDestroyed = false #txnDirty = new WeakSet<object>()
constructor( input: Input, onTransactionCommit: (cb: () => void) => void, format: Format, onDestroy: () => void = () => {}, queryComplete: true | ErroredQuery | Promise<true>, updateTTL: (ttl: TTL) => void, ) { this.#input = input this.#format = format this.#onDestroy = onDestroy this.#updateTTL = updateTTL this.#root = { '': format.singular ? undefined : [] } this.#data = shallowRef(this.#root) this.#status = ref(queryComplete === true ? 'complete' : 'error' in queryComplete ? 'error' : 'unknown') this.#error = ref(queryComplete !== true && 'error' in queryComplete ? makeError(queryComplete) : undefined) as Ref<QueryError | undefined>
input.setOutput(this)
for (const node of skipYields(input.fetch({}))) { this.#applyChange({ type: 'add', node }, true) }
this.#data.value = this.#root
onTransactionCommit(() => this.#flush())
if (queryComplete !== true && !('error' in queryComplete)) { void queryComplete.then(() => { this.#status.value = 'complete' this.#error.value = undefined }).catch((error: ErroredQuery) => { this.#status.value = 'error' this.#error.value = makeError(error) }) } }
get data(): Entry[string] { return this.#data.value[''] }
get status(): QueryStatus { return this.#status.value }
get error(): QueryError | undefined { return this.#error.value }
destroy(): void { if (!this.#isDestroyed) { this.#isDestroyed = true this.#onDestroy() } }
#applyChange(change: ViewChange, mutate: boolean | WeakSet<object> = this.#txnDirty): void { this.#root = applyChange( this.#root, change, this.#input.getSchema(), '', this.#format, false, mutate, ) }
push(change: Change): readonly never[] { this.#applyChange(materializeRelationships(change)) return Object.freeze([]) }
updateTTL(ttl: TTL): void { this.#updateTTL(ttl) }
#flush(): void { this.#txnDirty = new WeakSet<object>() this.#data.value = this.#root }}
function materializeRelationships(change: Change): ViewChange { switch (change[0]) { case 0: return { type: 'add', node: materializeNodeRelationships(change[1]), } case 1: return { type: 'remove', node: materializeNodeRelationships(change[1]), } case 2: return { type: 'edit', node: { row: change[1].row }, oldNode: { row: change[2].row }, } case 3: return { type: 'child', node: { row: change[1].row }, child: { relationshipName: change[2].relationshipName, change: materializeRelationships(change[2].change), }, } }}
function materializeNodeRelationships(node: Node): Node { const relationships: Record<string, () => Stream<Node>> = {}
for (const relationship in node.relationships) { const children = node.relationships[relationship] if (!children) { continue }
const materialized: Node[] = []
for (const child of skipYields(children())) { materialized.push(materializeNodeRelationships(child)) }
relationships[relationship] = () => materialized }
return { row: node.row, relationships, }}
function makeError(error: ErroredQuery): QueryError { const message = error.message ?? 'An unknown error occurred' return { type: error.error, message, ...(error.details ? { details: error.details } : {}), }}
export function vueViewFactory< TTable extends keyof TSchema['tables'] & string, TSchema extends Schema, TReturn,>( _query: Query<TTable, TSchema, TReturn>, input: Input, format: Format, onDestroy: () => void, onTransactionCommit: (cb: () => void) => void, queryComplete: true | ErroredQuery | Promise<true>, updateTTL: (ttl: TTL) => void,): VueView { return new VueView( input, onTransactionCommit, format, onDestroy, queryComplete, updateTTL, )}
vueViewFactory satisfies AnyViewFactory