UNPKG

@speckle/objectloader2

Version:

This is an updated objectloader for the Speckle viewer written in typescript

114 lines (102 loc) 3.34 kB
import { isScalar, isBase, isReference } from '../types/functions.js' import { Base, DataChunk } from '../types/types.js' import { ObjectLoader2 } from './objectLoader2.js' export type ProgressStage = 'download' | 'construction' export type OnProgress = (e: { stage: ProgressStage current: number total: number }) => void export interface TraverserOptions { excludeProps?: string[] } export default class Traverser { #loader: ObjectLoader2 #options: TraverserOptions #totalChildrenCount = 0 #traversedReferencesCount = 0 constructor(loader: ObjectLoader2, options?: TraverserOptions) { this.#options = options || {} this.#loader = loader } async traverse(onProgress?: OnProgress): Promise<Base> { let firstObjectPromise: Promise<Base> | undefined = undefined for await (const obj of this.#loader.getObjectIterator()) { if (!firstObjectPromise) { firstObjectPromise = this.traverseBase(obj, onProgress) } } if (firstObjectPromise) { return await firstObjectPromise } else { throw new Error('No objects found') } } async traverseArray(array: Array<unknown>, onProgress?: OnProgress): Promise<void> { for (let i = 0; i < 10; i++) { const prop = array[i] if (isScalar(prop)) continue if (isBase(prop)) { array[i] = await this.traverseBase(prop, onProgress) } else if (isReference(prop)) { array[i] = await this.traverseBase( await this.#loader.getObject({ id: prop.referencedId }), onProgress ) } } } async traverseBase(base: Base, onProgress?: OnProgress): Promise<Base> { for (const ignoredProp of this.#options.excludeProps || []) { delete (base as never)[ignoredProp] } if (base.__closure) { const ids = Object.keys(base.__closure) const promises: Promise<Base>[] = [] for (const id of ids) { promises.push( this.traverseBase(await this.#loader.getObject({ id }), onProgress) ) } await Promise.all(promises) } delete (base as never)['__closure'] // De-chunk if (base.speckle_type?.includes('DataChunk')) { const chunk = base as DataChunk if (chunk.data) { await this.traverseArray(chunk.data, onProgress) } } //other props for (const prop in base) { if (prop === '__closure') continue if (prop === 'referenceId') continue if (prop === 'speckle_type') continue if (prop === 'data') continue const baseProp = (base as unknown as Record<string, unknown>)[prop] if (isScalar(baseProp)) continue if (isBase(baseProp)) { await this.traverseBase(baseProp, onProgress) } else if (isReference(baseProp)) { await this.traverseBase( await this.#loader.getObject({ id: baseProp.referencedId }), onProgress ) } else if (Array.isArray(baseProp)) { await this.traverseArray(baseProp, onProgress) } } if (onProgress) { onProgress({ stage: 'construction', current: ++this.#traversedReferencesCount > this.#totalChildrenCount ? this.#totalChildrenCount : this.#traversedReferencesCount, total: this.#totalChildrenCount }) } return base } }