@speckle/objectloader 2.9.0 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.cjs CHANGED
@@ -6,12 +6,14 @@
6
6
  /** @type {import("eslint").Linter.Config} */
7
7
  const config = {
8
8
  env: {
9
- browser: true
9
+ browser: true,
10
+ node: true
10
11
  },
11
12
  parserOptions: {
12
- sourceType: 'module'
13
+ sourceType: 'module',
14
+ ecmaVersion: 2022
13
15
  },
14
- ignorePatterns: ['examples/browser/objectloader.web.js']
16
+ ignorePatterns: ['examples/browser/objectloader.web.js', 'types/**/*']
15
17
  }
16
18
 
17
19
  module.exports = config
@@ -1 +1 @@
1
- import"core-js/modules/es.symbol.description.js";import"core-js/modules/es.array.flat.js";import"core-js/modules/es.array.flat-map.js";import"core-js/modules/es.array.includes.js";import"core-js/modules/es.array.reduce.js";import"core-js/modules/es.array.reduce-right.js";import"core-js/modules/es.array.sort.js";import"core-js/modules/es.array.unscopables.flat.js";import"core-js/modules/es.array.unscopables.flat-map.js";import"core-js/modules/es.math.hypot.js";import"core-js/modules/es.object.from-entries.js";import"core-js/modules/es.promise.js";import"core-js/modules/es.promise.finally.js";import"core-js/modules/es.regexp.constructor.js";import"core-js/modules/es.regexp.exec.js";import"core-js/modules/es.regexp.flags.js";import"core-js/modules/es.string.replace.js";import"core-js/modules/es.typed-array.float32-array.js";import"core-js/modules/es.typed-array.float64-array.js";import"core-js/modules/es.typed-array.int8-array.js";import"core-js/modules/es.typed-array.int16-array.js";import"core-js/modules/es.typed-array.int32-array.js";import"core-js/modules/es.typed-array.uint8-array.js";import"core-js/modules/es.typed-array.uint8-clamped-array.js";import"core-js/modules/es.typed-array.uint16-array.js";import"core-js/modules/es.typed-array.uint32-array.js";import"core-js/modules/es.typed-array.from.js";import"core-js/modules/es.typed-array.of.js";import"core-js/modules/es.typed-array.set.js";import"core-js/modules/es.typed-array.sort.js";import"core-js/modules/esnext.aggregate-error.js";import"core-js/modules/esnext.array.last-index.js";import"core-js/modules/esnext.array.last-item.js";import"core-js/modules/esnext.composite-key.js";import"core-js/modules/esnext.composite-symbol.js";import"core-js/modules/esnext.global-this.js";import"core-js/modules/esnext.map.delete-all.js";import"core-js/modules/esnext.map.every.js";import"core-js/modules/esnext.map.filter.js";import"core-js/modules/esnext.map.find.js";import"core-js/modules/esnext.map.find-key.js";import"core-js/modules/esnext.map.from.js";import"core-js/modules/esnext.map.group-by.js";import"core-js/modules/esnext.map.includes.js";import"core-js/modules/esnext.map.key-by.js";import"core-js/modules/esnext.map.key-of.js";import"core-js/modules/esnext.map.map-keys.js";import"core-js/modules/esnext.map.map-values.js";import"core-js/modules/esnext.map.merge.js";import"core-js/modules/esnext.map.of.js";import"core-js/modules/esnext.map.reduce.js";import"core-js/modules/esnext.map.some.js";import"core-js/modules/esnext.map.update.js";import"core-js/modules/esnext.math.clamp.js";import"core-js/modules/esnext.math.deg-per-rad.js";import"core-js/modules/esnext.math.degrees.js";import"core-js/modules/esnext.math.fscale.js";import"core-js/modules/esnext.math.iaddh.js";import"core-js/modules/esnext.math.imulh.js";import"core-js/modules/esnext.math.isubh.js";import"core-js/modules/esnext.math.rad-per-deg.js";import"core-js/modules/esnext.math.radians.js";import"core-js/modules/esnext.math.scale.js";import"core-js/modules/esnext.math.seeded-prng.js";import"core-js/modules/esnext.math.signbit.js";import"core-js/modules/esnext.math.umulh.js";import"core-js/modules/esnext.number.from-string.js";import"core-js/modules/esnext.observable.js";import"core-js/modules/esnext.promise.all-settled.js";import"core-js/modules/esnext.promise.any.js";import"core-js/modules/esnext.promise.try.js";import"core-js/modules/esnext.reflect.define-metadata.js";import"core-js/modules/esnext.reflect.delete-metadata.js";import"core-js/modules/esnext.reflect.get-metadata.js";import"core-js/modules/esnext.reflect.get-metadata-keys.js";import"core-js/modules/esnext.reflect.get-own-metadata.js";import"core-js/modules/esnext.reflect.get-own-metadata-keys.js";import"core-js/modules/esnext.reflect.has-metadata.js";import"core-js/modules/esnext.reflect.has-own-metadata.js";import"core-js/modules/esnext.reflect.metadata.js";import"core-js/modules/esnext.set.add-all.js";import"core-js/modules/esnext.set.delete-all.js";import"core-js/modules/esnext.set.difference.js";import"core-js/modules/esnext.set.every.js";import"core-js/modules/esnext.set.filter.js";import"core-js/modules/esnext.set.find.js";import"core-js/modules/esnext.set.from.js";import"core-js/modules/esnext.set.intersection.js";import"core-js/modules/esnext.set.is-disjoint-from.js";import"core-js/modules/esnext.set.is-subset-of.js";import"core-js/modules/esnext.set.is-superset-of.js";import"core-js/modules/esnext.set.join.js";import"core-js/modules/esnext.set.map.js";import"core-js/modules/esnext.set.of.js";import"core-js/modules/esnext.set.reduce.js";import"core-js/modules/esnext.set.some.js";import"core-js/modules/esnext.set.symmetric-difference.js";import"core-js/modules/esnext.set.union.js";import"core-js/modules/esnext.string.at.js";import"core-js/modules/esnext.string.code-points.js";import"core-js/modules/esnext.string.match-all.js";import"core-js/modules/esnext.string.replace-all.js";import"core-js/modules/esnext.symbol.dispose.js";import"core-js/modules/esnext.symbol.observable.js";import"core-js/modules/esnext.symbol.pattern-match.js";import"core-js/modules/esnext.weak-map.delete-all.js";import"core-js/modules/esnext.weak-map.from.js";import"core-js/modules/esnext.weak-map.of.js";import"core-js/modules/esnext.weak-set.add-all.js";import"core-js/modules/esnext.weak-set.delete-all.js";import"core-js/modules/esnext.weak-set.from.js";import"core-js/modules/esnext.weak-set.of.js";import"core-js/modules/web.dom-collections.iterator.js";import"core-js/modules/web.immediate.js";import"core-js/modules/web.queue-microtask.js";import"core-js/modules/web.url.js";import"core-js/modules/web.url.to-json.js";import"core-js/modules/web.url-search-params.js";class e{constructor(e){let{serverUrl:s,streamId:t,token:o,objectId:r,options:i={enableCaching:!0,fullyTraverseArrays:!1,excludeProps:[],fetch:null}}=e;this.INTERVAL_MS=20,this.TIMEOUT_MS=18e4,this.serverUrl=s||window.location.origin,this.streamId=t,this.objectId=r,console.log("Object loader constructor called!");try{this.token=o||localStorage.getItem("AuthToken")}catch(e){}this.headers={Accept:"text/plain"},this.token&&(this.headers.Authorization="Bearer ".concat(this.token)),this.requestUrlRootObj="".concat(this.serverUrl,"/objects/").concat(this.streamId,"/").concat(this.objectId,"/single"),this.requestUrlChildren="".concat(this.serverUrl,"/api/getobjects/").concat(this.streamId),this.promises=[],this.intervals={},this.buffer=[],this.isLoading=!1,this.totalChildrenCount=0,this.traversedReferencesCount=0,this.options=i,this.options.numConnections=this.options.numConnections||4,this.cacheDB=null,this.lastAsyncPause=Date.now(),this.existingAsyncPause=null,this.preferredFetch=i.fetch,this.fetch=function(){const e=this.preferredFetch||fetch;return e(...arguments)}}async asyncPause(){Date.now()-this.lastAsyncPause>=100&&(this.lastAsyncPause=Date.now(),this.existingAsyncPause=new Promise((e=>setTimeout(e,0))),await this.existingAsyncPause,this.existingAsyncPause=null,Date.now()-this.lastAsyncPause>500&&console.log("Loader Event loop lag: ",Date.now()-this.lastAsyncPause))}dispose(){this.buffer=[],this.intervals.forEach((e=>clearInterval(e.interval)))}async getAndConstructObject(e){await this.downloadObjectsInBuffer(e);const s=await this.getObject(this.objectId);return this.traverseAndConstruct(s,e)}async downloadObjectsInBuffer(e){let s=!0,t=0;for await(const o of this.getObjectIterator())s&&(this.totalChildrenCount=o.totalChildrenCount,s=!1,this.isLoading=!0),t++,e&&e({stage:"download",current:t,total:this.totalChildrenCount});this.isLoading=!1}async traverseAndConstruct(e,s){if(e){if("object"!=typeof e)return e;if(Array.isArray(e)&&0!==e.length){var t,o;const r=[];for(const t of e){if("object"!=typeof t&&!this.options.fullyTraverseArrays)return e;const o=t.referencedId?await this.getObject(t.referencedId):t;t.referencedId&&s&&s({stage:"construction",current:++this.traversedReferencesCount>this.totalChildrenCount?this.totalChildrenCount:this.traversedReferencesCount,total:this.totalChildrenCount}),r.push(await this.traverseAndConstruct(o,s))}return null!==(t=r[0])&&void 0!==t&&null!==(o=t.speckle_type)&&void 0!==o&&o.toLowerCase().includes("datachunk")?r.reduce(((e,s)=>e.concat(s.data)),[]):r}for(const s of this.options.excludeProps)delete e[s];for(const t in e)"object"==typeof e[t]&&null!==e[t]&&(e[t].referencedId&&(e[t]=await this.getObject(e[t].referencedId),s&&s({stage:"construction",current:++this.traversedReferencesCount>this.totalChildrenCount?this.totalChildrenCount:this.traversedReferencesCount,total:this.totalChildrenCount})),e[t]=await this.traverseAndConstruct(e[t],s));return e}}async getObject(e){if(this.buffer[e])return this.buffer[e];return new Promise(((s,t)=>{if(this.promises.push({id:e,resolve:s,reject:t}),this.intervals[e])this.intervals[e].elapsed=0;else{const s=setInterval(this.tryResolvePromise.bind(this),this.INTERVAL_MS,e);this.intervals[e]={interval:s,elapsed:0}}}))}tryResolvePromise(e){if(this.intervals[e].elapsed+=this.INTERVAL_MS,this.buffer[e]){for(const s of this.promises.filter((s=>s.id===e)))s.resolve(this.buffer[e]);return clearInterval(this.intervals[e].interval),void delete this.intervals[e]}this.intervals[e].elapsed>this.TIMEOUT_MS&&(console.warn("Timeout resolving ".concat(e,". HIC SVNT DRACONES.")),clearInterval(this.intervals[e].interval),this.promises.filter((s=>s.id===e)).forEach((e=>e.reject())),this.promises=this.promises.filter((e=>e.id!=e.id)))}async*getObjectIterator(){const e=Date.now();let s=0;for await(const e of this.getRawObjectIterator()){const{id:t,obj:o}=this.processLine(e);this.buffer[t]=o,s+=1,yield o}console.log("Loaded ".concat(s," objects in: ").concat((Date.now()-e)/1e3))}processLine(e){const s=e.split("\t");return{id:s[0],obj:JSON.parse(s[1])}}async*getRawObjectIterator(){if(this.options.enableCaching&&window.indexedDB&&null===this.cacheDB){await function(){if(navigator.userAgentData||!/Safari\//.test(navigator.userAgent)||/Chrom(e|ium)\//.test(navigator.userAgent)||!indexedDB.databases)return Promise.resolve();let e;return new Promise((s=>{const t=()=>indexedDB.databases().finally(s);e=setInterval(t,100),t()})).finally((()=>clearInterval(e)))}();const e=indexedDB.open("speckle-object-cache",1);e.onupgradeneeded=()=>e.result.createObjectStore("objects"),this.cacheDB=await this.promisifyIdbRequest(e)}const e=await this.getRawRootObject();yield"".concat(this.objectId,"\t").concat(e);const s=JSON.parse(e);if(!s.__closure)return;let t=Object.keys(s.__closure).sort(((e,t)=>s.__closure[e]-s.__closure[t]));if(0===t.length)return;let o=[];if(t.length>50){const e=[[],[],[],[]];let r=0;for(;r<.05*t.length;r++)e[0].push(t[r]);for(;r<.2*t.length;r++)e[1].push(t[r]);for(;r<.6*t.length;r++)e[2].push(t[r]);for(;r<t.length;r++)e[3].push(t[r]);console.log("Cache check for: ",e);const i=[];let n=this.cacheGetObjects(e[0]);for(let t=0;t<4;t++){const o=await n;t<3&&(n=this.cacheGetObjects(e[t+1]));const r=Object.keys(o).sort(((e,t)=>s.__closure[e]-s.__closure[t]));for(const e of r)yield"".concat(e,"\t").concat(o[e]);const a=e[t].filter((e=>!(e in o)));i.push(...a)}if(0===i.length)return;if(i.length<=50)o.push(i);else{for(o=[[],[],[],[]],r=0;r<.05*i.length;r++)o[0].push(i[r]);for(;r<.2*i.length;r++)o[1].push(i[r]);for(;r<.6*i.length;r++)o[2].push(i[r]);for(;r<i.length;r++)o[3].push(i[r])}}else{const e=await this.cacheGetObjects(t),r=Object.keys(e).sort(((e,t)=>s.__closure[e]-s.__closure[t]));for(const s of r)yield"".concat(s,"\t").concat(e[s]);if(t=t.filter((s=>!(s in e))),0===t.length)return;o.push(t)}const r=[],i=[],n=[],a=[],c=[];for(let e=0;e<o.length;e++)r.push(new TextDecoder),i.push(null),n.push(null),a.push(""),c.push(!1),this.fetch(this.requestUrlChildren,{method:"POST",headers:{...this.headers,"Content-Type":"application/json"},body:JSON.stringify({objects:JSON.stringify(o[e])})}).then((s=>{const t=s.body.getReader();i[e]=t;const o=t.read().then((s=>(s.reqId=e,s)));n[e]=o}));for(;;){const e=n.filter((e=>!!e));if(0===e.length){if(c.every((e=>e)))break;await new Promise((e=>{setTimeout(e,10)}));continue}const s=await Promise.any(e);let{value:t,done:o,reqId:l}=s;if(c[l]=o,o)a[l].length>0&&(yield a[l],a[l]=""),n[l]=null;else{const e=i[l].read().then((e=>(e.reqId=l,e)));n[l]=e}if(!t)continue;t=r[l].decode(t);const m=(a[l]+t).split(/\r\n|\n|\r/),d=m.pop();a[l]=d;for(const e of m)yield e;this.cacheStoreObjects(m)}}async getRawRootObject(){const e=await this.cacheGetObjects([this.objectId]);if(e[this.objectId])return e[this.objectId];const s=await this.fetch(this.requestUrlRootObj,{headers:this.headers}),t=await s.text();return this.cacheStoreObjects(["".concat(this.objectId,"\t").concat(t)]),t}promisifyIdbRequest(e){return new Promise(((s,t)=>{e.oncomplete=e.onsuccess=()=>s(e.result),e.onabort=e.onerror=()=>t(e.error)}))}async cacheGetObjects(e){if(!this.options.enableCaching||!window.indexedDB)return{};const s={};for(let t=0;t<e.length;t+=500){const o=e.slice(t,t+500),r=this.cacheDB.transaction("objects","readonly").objectStore("objects"),i=o.map((e=>this.promisifyIdbRequest(r.get(e)).then((s=>({id:e,data:s}))))),n=await Promise.all(i);for(const e of n)e.data&&(s[e.id]=e.data)}return s}cacheStoreObjects(e){if(!this.options.enableCaching||!window.indexedDB)return{};const s=this.cacheDB.transaction("objects","readwrite").objectStore("objects");for(const t of e){const e=t.split("\t");s.put(e[1],e[0])}return this.promisifyIdbRequest(s.transaction)}}export{e as default};
1
+ import"core-js/modules/es.symbol.description.js";import"core-js/modules/es.array.flat.js";import"core-js/modules/es.array.flat-map.js";import"core-js/modules/es.array.includes.js";import"core-js/modules/es.array.reduce.js";import"core-js/modules/es.array.reduce-right.js";import"core-js/modules/es.array.sort.js";import"core-js/modules/es.array.unscopables.flat.js";import"core-js/modules/es.array.unscopables.flat-map.js";import"core-js/modules/es.math.hypot.js";import"core-js/modules/es.object.from-entries.js";import"core-js/modules/es.promise.js";import"core-js/modules/es.promise.finally.js";import"core-js/modules/es.regexp.constructor.js";import"core-js/modules/es.regexp.exec.js";import"core-js/modules/es.regexp.flags.js";import"core-js/modules/es.string.replace.js";import"core-js/modules/es.typed-array.float32-array.js";import"core-js/modules/es.typed-array.float64-array.js";import"core-js/modules/es.typed-array.int8-array.js";import"core-js/modules/es.typed-array.int16-array.js";import"core-js/modules/es.typed-array.int32-array.js";import"core-js/modules/es.typed-array.uint8-array.js";import"core-js/modules/es.typed-array.uint8-clamped-array.js";import"core-js/modules/es.typed-array.uint16-array.js";import"core-js/modules/es.typed-array.uint32-array.js";import"core-js/modules/es.typed-array.from.js";import"core-js/modules/es.typed-array.of.js";import"core-js/modules/es.typed-array.set.js";import"core-js/modules/es.typed-array.sort.js";import"core-js/modules/esnext.aggregate-error.js";import"core-js/modules/esnext.array.last-index.js";import"core-js/modules/esnext.array.last-item.js";import"core-js/modules/esnext.composite-key.js";import"core-js/modules/esnext.composite-symbol.js";import"core-js/modules/esnext.global-this.js";import"core-js/modules/esnext.map.delete-all.js";import"core-js/modules/esnext.map.every.js";import"core-js/modules/esnext.map.filter.js";import"core-js/modules/esnext.map.find.js";import"core-js/modules/esnext.map.find-key.js";import"core-js/modules/esnext.map.from.js";import"core-js/modules/esnext.map.group-by.js";import"core-js/modules/esnext.map.includes.js";import"core-js/modules/esnext.map.key-by.js";import"core-js/modules/esnext.map.key-of.js";import"core-js/modules/esnext.map.map-keys.js";import"core-js/modules/esnext.map.map-values.js";import"core-js/modules/esnext.map.merge.js";import"core-js/modules/esnext.map.of.js";import"core-js/modules/esnext.map.reduce.js";import"core-js/modules/esnext.map.some.js";import"core-js/modules/esnext.map.update.js";import"core-js/modules/esnext.math.clamp.js";import"core-js/modules/esnext.math.deg-per-rad.js";import"core-js/modules/esnext.math.degrees.js";import"core-js/modules/esnext.math.fscale.js";import"core-js/modules/esnext.math.iaddh.js";import"core-js/modules/esnext.math.imulh.js";import"core-js/modules/esnext.math.isubh.js";import"core-js/modules/esnext.math.rad-per-deg.js";import"core-js/modules/esnext.math.radians.js";import"core-js/modules/esnext.math.scale.js";import"core-js/modules/esnext.math.seeded-prng.js";import"core-js/modules/esnext.math.signbit.js";import"core-js/modules/esnext.math.umulh.js";import"core-js/modules/esnext.number.from-string.js";import"core-js/modules/esnext.observable.js";import"core-js/modules/esnext.promise.all-settled.js";import"core-js/modules/esnext.promise.any.js";import"core-js/modules/esnext.promise.try.js";import"core-js/modules/esnext.reflect.define-metadata.js";import"core-js/modules/esnext.reflect.delete-metadata.js";import"core-js/modules/esnext.reflect.get-metadata.js";import"core-js/modules/esnext.reflect.get-metadata-keys.js";import"core-js/modules/esnext.reflect.get-own-metadata.js";import"core-js/modules/esnext.reflect.get-own-metadata-keys.js";import"core-js/modules/esnext.reflect.has-metadata.js";import"core-js/modules/esnext.reflect.has-own-metadata.js";import"core-js/modules/esnext.reflect.metadata.js";import"core-js/modules/esnext.set.add-all.js";import"core-js/modules/esnext.set.delete-all.js";import"core-js/modules/esnext.set.difference.js";import"core-js/modules/esnext.set.every.js";import"core-js/modules/esnext.set.filter.js";import"core-js/modules/esnext.set.find.js";import"core-js/modules/esnext.set.from.js";import"core-js/modules/esnext.set.intersection.js";import"core-js/modules/esnext.set.is-disjoint-from.js";import"core-js/modules/esnext.set.is-subset-of.js";import"core-js/modules/esnext.set.is-superset-of.js";import"core-js/modules/esnext.set.join.js";import"core-js/modules/esnext.set.map.js";import"core-js/modules/esnext.set.of.js";import"core-js/modules/esnext.set.reduce.js";import"core-js/modules/esnext.set.some.js";import"core-js/modules/esnext.set.symmetric-difference.js";import"core-js/modules/esnext.set.union.js";import"core-js/modules/esnext.string.at.js";import"core-js/modules/esnext.string.code-points.js";import"core-js/modules/esnext.string.match-all.js";import"core-js/modules/esnext.string.replace-all.js";import"core-js/modules/esnext.symbol.dispose.js";import"core-js/modules/esnext.symbol.observable.js";import"core-js/modules/esnext.symbol.pattern-match.js";import"core-js/modules/esnext.weak-map.delete-all.js";import"core-js/modules/esnext.weak-map.from.js";import"core-js/modules/esnext.weak-map.of.js";import"core-js/modules/esnext.weak-set.add-all.js";import"core-js/modules/esnext.weak-set.delete-all.js";import"core-js/modules/esnext.weak-set.from.js";import"core-js/modules/esnext.weak-set.of.js";import"core-js/modules/web.dom-collections.iterator.js";import"core-js/modules/web.immediate.js";import"core-js/modules/web.queue-microtask.js";import"core-js/modules/web.url.js";import"core-js/modules/web.url.to-json.js";import"core-js/modules/web.url-search-params.js";import{SafeLocalStorage as e}from"@speckle/shared";function s(e,s,t){return s in e?Object.defineProperty(e,s,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[s]=t,e}class t extends Error{constructor(e,s){e||(e=new.target.defaultMessage),super(e,s)}}s(t,"defaultMessage","Unexpected error occurred");class o extends t{}s(o,"defaultMessage","Object loader configured incorrectly!");class r extends t{}s(r,"defaultMessage","Object loader encountered a runtime problem!");class i{constructor(s){var t;let{serverUrl:i,streamId:n,token:a,objectId:c,options:l={enableCaching:!0,fullyTraverseArrays:!1,excludeProps:[],fetch:null,customLogger:void 0,customWarner:void 0}}=s;if(this.logger=l.customLogger||console.log,this.warner=l.customWarner||console.warn,this.INTERVAL_MS=20,this.TIMEOUT_MS=18e4,this.serverUrl=i||(null===globalThis||void 0===globalThis||null===(t=globalThis.location)||void 0===t?void 0:t.origin),!this.serverUrl)throw new o("Invalid serverUrl specified!");if(this.streamId=n,this.objectId=c,!this.streamId)throw new o("Invalid streamId specified!");if(!this.objectId)throw new o("Invalid objectId specified!");this.logger("Object loader constructor called!");try{this.token=a||e.get("AuthToken")}catch(e){}this.headers={Accept:"text/plain"},this.token&&(this.headers.Authorization="Bearer ".concat(this.token)),this.requestUrlRootObj="".concat(this.serverUrl,"/objects/").concat(this.streamId,"/").concat(this.objectId,"/single"),this.requestUrlChildren="".concat(this.serverUrl,"/api/getobjects/").concat(this.streamId),this.promises=[],this.intervals={},this.buffer=[],this.isLoading=!1,this.totalChildrenCount=0,this.traversedReferencesCount=0,this.options=l,this.options.numConnections=this.options.numConnections||4,this.cacheDB=null,this.lastAsyncPause=Date.now(),this.existingAsyncPause=null,this.preferredFetch=l.fetch,this.fetch=function(){const e=this.preferredFetch||fetch;if(!e)throw new r("Couldn't find fetch implementation! If running in a node environment, make sure you pass it in through the constructor!");return e(...arguments)}}async asyncPause(){Date.now()-this.lastAsyncPause>=100&&(this.lastAsyncPause=Date.now(),this.existingAsyncPause=new Promise((e=>setTimeout(e,0))),await this.existingAsyncPause,this.existingAsyncPause=null,Date.now()-this.lastAsyncPause>500&&this.logger("Loader Event loop lag: ",Date.now()-this.lastAsyncPause))}dispose(){this.buffer=[],this.intervals.forEach((e=>clearInterval(e.interval)))}async getAndConstructObject(e){await this.downloadObjectsInBuffer(e);const s=await this.getObject(this.objectId);return this.traverseAndConstruct(s,e)}async downloadObjectsInBuffer(e){let s=!0,t=0;for await(const o of this.getObjectIterator())s&&(this.totalChildrenCount=o.totalChildrenCount,s=!1,this.isLoading=!0),t++,e&&e({stage:"download",current:t,total:this.totalChildrenCount});this.isLoading=!1}async traverseAndConstruct(e,s){if(e){if("object"!=typeof e)return e;if(Array.isArray(e)&&0!==e.length){var t,o;const r=[];for(const t of e){if("object"!=typeof t&&!this.options.fullyTraverseArrays)return e;const o=t.referencedId?await this.getObject(t.referencedId):t;t.referencedId&&s&&s({stage:"construction",current:++this.traversedReferencesCount>this.totalChildrenCount?this.totalChildrenCount:this.traversedReferencesCount,total:this.totalChildrenCount}),r.push(await this.traverseAndConstruct(o,s))}return null!==(t=r[0])&&void 0!==t&&null!==(o=t.speckle_type)&&void 0!==o&&o.toLowerCase().includes("datachunk")?r.reduce(((e,s)=>e.concat(s.data)),[]):r}for(const s of this.options.excludeProps)delete e[s];for(const t in e)"object"==typeof e[t]&&null!==e[t]&&(e[t].referencedId&&(e[t]=await this.getObject(e[t].referencedId),s&&s({stage:"construction",current:++this.traversedReferencesCount>this.totalChildrenCount?this.totalChildrenCount:this.traversedReferencesCount,total:this.totalChildrenCount})),e[t]=await this.traverseAndConstruct(e[t],s));return e}}async getObject(e){if(this.buffer[e])return this.buffer[e];return new Promise(((s,t)=>{if(this.promises.push({id:e,resolve:s,reject:t}),this.intervals[e])this.intervals[e].elapsed=0;else{const s=setInterval(this.tryResolvePromise.bind(this),this.INTERVAL_MS,e);this.intervals[e]={interval:s,elapsed:0}}}))}tryResolvePromise(e){if(this.intervals[e].elapsed+=this.INTERVAL_MS,this.buffer[e]){for(const s of this.promises.filter((s=>s.id===e)))s.resolve(this.buffer[e]);return clearInterval(this.intervals[e].interval),void delete this.intervals[e]}this.intervals[e].elapsed>this.TIMEOUT_MS&&(this.warner("Timeout resolving ".concat(e,". HIC SVNT DRACONES.")),clearInterval(this.intervals[e].interval),this.promises.filter((s=>s.id===e)).forEach((e=>e.reject())),this.promises=this.promises.filter((e=>e.id!=e.id)))}async*getObjectIterator(){const e=Date.now();let s=0;for await(const e of this.getRawObjectIterator()){const{id:t,obj:o}=this.processLine(e);this.buffer[t]=o,s+=1,yield o}this.logger("Loaded ".concat(s," objects in: ").concat((Date.now()-e)/1e3))}processLine(e){const s=e.split("\t");return{id:s[0],obj:JSON.parse(s[1])}}supportsCache(){return!(!this.options.enableCaching||!globalThis.indexedDB)}async setupCacheDb(){if(!this.supportsCache()||null!==this.cacheDB)return;await function(){if(navigator.userAgentData||!/Safari\//.test(navigator.userAgent)||/Chrom(e|ium)\//.test(navigator.userAgent)||!indexedDB.databases)return Promise.resolve();let e;return new Promise((s=>{const t=()=>indexedDB.databases().finally(s);e=setInterval(t,100),t()})).finally((()=>clearInterval(e)))}();const e=indexedDB.open("speckle-object-cache",1);e.onupgradeneeded=()=>e.result.createObjectStore("objects"),this.cacheDB=await this.promisifyIdbRequest(e)}async*getRawObjectIterator(){await this.setupCacheDb();const e=await this.getRawRootObject();yield"".concat(this.objectId,"\t").concat(e);const s=JSON.parse(e);if(!s.__closure)return;let t=Object.keys(s.__closure).filter((e=>!e.includes("blob"))).sort(((e,t)=>s.__closure[e]-s.__closure[t]));if(0===t.length)return;let o=[];if(t.length>50){const e=[[],[],[],[]];let r=0;for(;r<.05*t.length;r++)e[0].push(t[r]);for(;r<.2*t.length;r++)e[1].push(t[r]);for(;r<.6*t.length;r++)e[2].push(t[r]);for(;r<t.length;r++)e[3].push(t[r]);this.logger("Cache check for: ",e);const i=[];let n=this.cacheGetObjects(e[0]);for(let t=0;t<4;t++){const o=await n;t<3&&(n=this.cacheGetObjects(e[t+1]));const r=Object.keys(o).sort(((e,t)=>s.__closure[e]-s.__closure[t]));for(const e of r)yield"".concat(e,"\t").concat(o[e]);const a=e[t].filter((e=>!(e in o)));i.push(...a)}if(0===i.length)return;if(i.length<=50)o.push(i);else{for(o=[[],[],[],[]],r=0;r<.05*i.length;r++)o[0].push(i[r]);for(;r<.2*i.length;r++)o[1].push(i[r]);for(;r<.6*i.length;r++)o[2].push(i[r]);for(;r<i.length;r++)o[3].push(i[r])}}else{const e=await this.cacheGetObjects(t),r=Object.keys(e).sort(((e,t)=>s.__closure[e]-s.__closure[t]));for(const s of r)yield"".concat(s,"\t").concat(e[s]);if(t=t.filter((s=>!(s in e))),0===t.length)return;o.push(t)}const r=[],i=[],n=[],a=[],c=[];for(let e=0;e<o.length;e++)r.push(new TextDecoder),i.push(null),n.push(null),a.push(""),c.push(!1),this.fetch(this.requestUrlChildren,{method:"POST",headers:{...this.headers,"Content-Type":"application/json"},body:JSON.stringify({objects:JSON.stringify(o[e])})}).then((s=>{s.body.getReader&&(s.body.iterator=async function*(){const e=this.getReader();for(;;){const s=await e.read();if(s.done)return s.value;yield s.value}});const t=s.body.iterator();i[e]=t;const o=t.next().then((s=>(s.reqId=e,s)));n[e]=o}));for(;;){const e=n.filter((e=>!!e));if(0===e.length){if(c.every((e=>e)))break;await new Promise((e=>{setTimeout(e,10)}));continue}const s=await Promise.any(e);let{value:t,done:o,reqId:l}=s;if(c[l]=o,o)a[l].length>0&&(yield a[l],a[l]=""),n[l]=null;else{const e=i[l].next().then((e=>(e.reqId=l,e)));n[l]=e}if(!t)continue;t=r[l].decode(t);const m=(a[l]+t).split(/\r\n|\n|\r/),d=m.pop();a[l]=d;for(const e of m)yield e;this.cacheStoreObjects(m)}}async getRawRootObject(){const e=await this.cacheGetObjects([this.objectId]);if(e[this.objectId])return e[this.objectId];const s=await this.fetch(this.requestUrlRootObj,{headers:this.headers}),t=await s.text();return this.cacheStoreObjects(["".concat(this.objectId,"\t").concat(t)]),t}promisifyIdbRequest(e){return new Promise(((s,t)=>{e.oncomplete=e.onsuccess=()=>s(e.result),e.onabort=e.onerror=()=>t(e.error)}))}async cacheGetObjects(e){if(!this.supportsCache())return{};const s={};for(let t=0;t<e.length;t+=500){const o=e.slice(t,t+500),r=this.cacheDB.transaction("objects","readonly").objectStore("objects"),i=o.map((e=>this.promisifyIdbRequest(r.get(e)).then((s=>({id:e,data:s}))))),n=await Promise.all(i);for(const e of n)e.data&&(s[e.id]=e.data)}return s}cacheStoreObjects(e){if(!this.supportsCache())return{};const s=this.cacheDB.transaction("objects","readwrite").objectStore("objects");for(const t of e){const e=t.split("\t");s.put(e[1],e[0])}return this.promisifyIdbRequest(s.transaction)}}export{i as default};
@@ -1 +1 @@
1
- "use strict";require("core-js/modules/es.symbol.description.js"),require("core-js/modules/es.array.flat.js"),require("core-js/modules/es.array.flat-map.js"),require("core-js/modules/es.array.includes.js"),require("core-js/modules/es.array.reduce.js"),require("core-js/modules/es.array.reduce-right.js"),require("core-js/modules/es.array.sort.js"),require("core-js/modules/es.array.unscopables.flat.js"),require("core-js/modules/es.array.unscopables.flat-map.js"),require("core-js/modules/es.math.hypot.js"),require("core-js/modules/es.object.from-entries.js"),require("core-js/modules/es.promise.js"),require("core-js/modules/es.promise.finally.js"),require("core-js/modules/es.regexp.constructor.js"),require("core-js/modules/es.regexp.exec.js"),require("core-js/modules/es.regexp.flags.js"),require("core-js/modules/es.string.replace.js"),require("core-js/modules/es.typed-array.float32-array.js"),require("core-js/modules/es.typed-array.float64-array.js"),require("core-js/modules/es.typed-array.int8-array.js"),require("core-js/modules/es.typed-array.int16-array.js"),require("core-js/modules/es.typed-array.int32-array.js"),require("core-js/modules/es.typed-array.uint8-array.js"),require("core-js/modules/es.typed-array.uint8-clamped-array.js"),require("core-js/modules/es.typed-array.uint16-array.js"),require("core-js/modules/es.typed-array.uint32-array.js"),require("core-js/modules/es.typed-array.from.js"),require("core-js/modules/es.typed-array.of.js"),require("core-js/modules/es.typed-array.set.js"),require("core-js/modules/es.typed-array.sort.js"),require("core-js/modules/esnext.aggregate-error.js"),require("core-js/modules/esnext.array.last-index.js"),require("core-js/modules/esnext.array.last-item.js"),require("core-js/modules/esnext.composite-key.js"),require("core-js/modules/esnext.composite-symbol.js"),require("core-js/modules/esnext.global-this.js"),require("core-js/modules/esnext.map.delete-all.js"),require("core-js/modules/esnext.map.every.js"),require("core-js/modules/esnext.map.filter.js"),require("core-js/modules/esnext.map.find.js"),require("core-js/modules/esnext.map.find-key.js"),require("core-js/modules/esnext.map.from.js"),require("core-js/modules/esnext.map.group-by.js"),require("core-js/modules/esnext.map.includes.js"),require("core-js/modules/esnext.map.key-by.js"),require("core-js/modules/esnext.map.key-of.js"),require("core-js/modules/esnext.map.map-keys.js"),require("core-js/modules/esnext.map.map-values.js"),require("core-js/modules/esnext.map.merge.js"),require("core-js/modules/esnext.map.of.js"),require("core-js/modules/esnext.map.reduce.js"),require("core-js/modules/esnext.map.some.js"),require("core-js/modules/esnext.map.update.js"),require("core-js/modules/esnext.math.clamp.js"),require("core-js/modules/esnext.math.deg-per-rad.js"),require("core-js/modules/esnext.math.degrees.js"),require("core-js/modules/esnext.math.fscale.js"),require("core-js/modules/esnext.math.iaddh.js"),require("core-js/modules/esnext.math.imulh.js"),require("core-js/modules/esnext.math.isubh.js"),require("core-js/modules/esnext.math.rad-per-deg.js"),require("core-js/modules/esnext.math.radians.js"),require("core-js/modules/esnext.math.scale.js"),require("core-js/modules/esnext.math.seeded-prng.js"),require("core-js/modules/esnext.math.signbit.js"),require("core-js/modules/esnext.math.umulh.js"),require("core-js/modules/esnext.number.from-string.js"),require("core-js/modules/esnext.observable.js"),require("core-js/modules/esnext.promise.all-settled.js"),require("core-js/modules/esnext.promise.any.js"),require("core-js/modules/esnext.promise.try.js"),require("core-js/modules/esnext.reflect.define-metadata.js"),require("core-js/modules/esnext.reflect.delete-metadata.js"),require("core-js/modules/esnext.reflect.get-metadata.js"),require("core-js/modules/esnext.reflect.get-metadata-keys.js"),require("core-js/modules/esnext.reflect.get-own-metadata.js"),require("core-js/modules/esnext.reflect.get-own-metadata-keys.js"),require("core-js/modules/esnext.reflect.has-metadata.js"),require("core-js/modules/esnext.reflect.has-own-metadata.js"),require("core-js/modules/esnext.reflect.metadata.js"),require("core-js/modules/esnext.set.add-all.js"),require("core-js/modules/esnext.set.delete-all.js"),require("core-js/modules/esnext.set.difference.js"),require("core-js/modules/esnext.set.every.js"),require("core-js/modules/esnext.set.filter.js"),require("core-js/modules/esnext.set.find.js"),require("core-js/modules/esnext.set.from.js"),require("core-js/modules/esnext.set.intersection.js"),require("core-js/modules/esnext.set.is-disjoint-from.js"),require("core-js/modules/esnext.set.is-subset-of.js"),require("core-js/modules/esnext.set.is-superset-of.js"),require("core-js/modules/esnext.set.join.js"),require("core-js/modules/esnext.set.map.js"),require("core-js/modules/esnext.set.of.js"),require("core-js/modules/esnext.set.reduce.js"),require("core-js/modules/esnext.set.some.js"),require("core-js/modules/esnext.set.symmetric-difference.js"),require("core-js/modules/esnext.set.union.js"),require("core-js/modules/esnext.string.at.js"),require("core-js/modules/esnext.string.code-points.js"),require("core-js/modules/esnext.string.match-all.js"),require("core-js/modules/esnext.string.replace-all.js"),require("core-js/modules/esnext.symbol.dispose.js"),require("core-js/modules/esnext.symbol.observable.js"),require("core-js/modules/esnext.symbol.pattern-match.js"),require("core-js/modules/esnext.weak-map.delete-all.js"),require("core-js/modules/esnext.weak-map.from.js"),require("core-js/modules/esnext.weak-map.of.js"),require("core-js/modules/esnext.weak-set.add-all.js"),require("core-js/modules/esnext.weak-set.delete-all.js"),require("core-js/modules/esnext.weak-set.from.js"),require("core-js/modules/esnext.weak-set.of.js"),require("core-js/modules/web.dom-collections.iterator.js"),require("core-js/modules/web.immediate.js"),require("core-js/modules/web.queue-microtask.js"),require("core-js/modules/web.url.js"),require("core-js/modules/web.url.to-json.js"),require("core-js/modules/web.url-search-params.js");module.exports=class{constructor(e){let{serverUrl:s,streamId:r,token:t,objectId:o,options:i={enableCaching:!0,fullyTraverseArrays:!1,excludeProps:[],fetch:null}}=e;this.INTERVAL_MS=20,this.TIMEOUT_MS=18e4,this.serverUrl=s||window.location.origin,this.streamId=r,this.objectId=o,console.log("Object loader constructor called!");try{this.token=t||localStorage.getItem("AuthToken")}catch(e){}this.headers={Accept:"text/plain"},this.token&&(this.headers.Authorization="Bearer ".concat(this.token)),this.requestUrlRootObj="".concat(this.serverUrl,"/objects/").concat(this.streamId,"/").concat(this.objectId,"/single"),this.requestUrlChildren="".concat(this.serverUrl,"/api/getobjects/").concat(this.streamId),this.promises=[],this.intervals={},this.buffer=[],this.isLoading=!1,this.totalChildrenCount=0,this.traversedReferencesCount=0,this.options=i,this.options.numConnections=this.options.numConnections||4,this.cacheDB=null,this.lastAsyncPause=Date.now(),this.existingAsyncPause=null,this.preferredFetch=i.fetch,this.fetch=function(){const e=this.preferredFetch||fetch;return e(...arguments)}}async asyncPause(){Date.now()-this.lastAsyncPause>=100&&(this.lastAsyncPause=Date.now(),this.existingAsyncPause=new Promise((e=>setTimeout(e,0))),await this.existingAsyncPause,this.existingAsyncPause=null,Date.now()-this.lastAsyncPause>500&&console.log("Loader Event loop lag: ",Date.now()-this.lastAsyncPause))}dispose(){this.buffer=[],this.intervals.forEach((e=>clearInterval(e.interval)))}async getAndConstructObject(e){await this.downloadObjectsInBuffer(e);const s=await this.getObject(this.objectId);return this.traverseAndConstruct(s,e)}async downloadObjectsInBuffer(e){let s=!0,r=0;for await(const t of this.getObjectIterator())s&&(this.totalChildrenCount=t.totalChildrenCount,s=!1,this.isLoading=!0),r++,e&&e({stage:"download",current:r,total:this.totalChildrenCount});this.isLoading=!1}async traverseAndConstruct(e,s){if(e){if("object"!=typeof e)return e;if(Array.isArray(e)&&0!==e.length){var r,t;const o=[];for(const r of e){if("object"!=typeof r&&!this.options.fullyTraverseArrays)return e;const t=r.referencedId?await this.getObject(r.referencedId):r;r.referencedId&&s&&s({stage:"construction",current:++this.traversedReferencesCount>this.totalChildrenCount?this.totalChildrenCount:this.traversedReferencesCount,total:this.totalChildrenCount}),o.push(await this.traverseAndConstruct(t,s))}return null!==(r=o[0])&&void 0!==r&&null!==(t=r.speckle_type)&&void 0!==t&&t.toLowerCase().includes("datachunk")?o.reduce(((e,s)=>e.concat(s.data)),[]):o}for(const s of this.options.excludeProps)delete e[s];for(const r in e)"object"==typeof e[r]&&null!==e[r]&&(e[r].referencedId&&(e[r]=await this.getObject(e[r].referencedId),s&&s({stage:"construction",current:++this.traversedReferencesCount>this.totalChildrenCount?this.totalChildrenCount:this.traversedReferencesCount,total:this.totalChildrenCount})),e[r]=await this.traverseAndConstruct(e[r],s));return e}}async getObject(e){if(this.buffer[e])return this.buffer[e];return new Promise(((s,r)=>{if(this.promises.push({id:e,resolve:s,reject:r}),this.intervals[e])this.intervals[e].elapsed=0;else{const s=setInterval(this.tryResolvePromise.bind(this),this.INTERVAL_MS,e);this.intervals[e]={interval:s,elapsed:0}}}))}tryResolvePromise(e){if(this.intervals[e].elapsed+=this.INTERVAL_MS,this.buffer[e]){for(const s of this.promises.filter((s=>s.id===e)))s.resolve(this.buffer[e]);return clearInterval(this.intervals[e].interval),void delete this.intervals[e]}this.intervals[e].elapsed>this.TIMEOUT_MS&&(console.warn("Timeout resolving ".concat(e,". HIC SVNT DRACONES.")),clearInterval(this.intervals[e].interval),this.promises.filter((s=>s.id===e)).forEach((e=>e.reject())),this.promises=this.promises.filter((e=>e.id!=e.id)))}async*getObjectIterator(){const e=Date.now();let s=0;for await(const e of this.getRawObjectIterator()){const{id:r,obj:t}=this.processLine(e);this.buffer[r]=t,s+=1,yield t}console.log("Loaded ".concat(s," objects in: ").concat((Date.now()-e)/1e3))}processLine(e){const s=e.split("\t");return{id:s[0],obj:JSON.parse(s[1])}}async*getRawObjectIterator(){if(this.options.enableCaching&&window.indexedDB&&null===this.cacheDB){await function(){if(navigator.userAgentData||!/Safari\//.test(navigator.userAgent)||/Chrom(e|ium)\//.test(navigator.userAgent)||!indexedDB.databases)return Promise.resolve();let e;return new Promise((s=>{const r=()=>indexedDB.databases().finally(s);e=setInterval(r,100),r()})).finally((()=>clearInterval(e)))}();const e=indexedDB.open("speckle-object-cache",1);e.onupgradeneeded=()=>e.result.createObjectStore("objects"),this.cacheDB=await this.promisifyIdbRequest(e)}const e=await this.getRawRootObject();yield"".concat(this.objectId,"\t").concat(e);const s=JSON.parse(e);if(!s.__closure)return;let r=Object.keys(s.__closure).sort(((e,r)=>s.__closure[e]-s.__closure[r]));if(0===r.length)return;let t=[];if(r.length>50){const e=[[],[],[],[]];let o=0;for(;o<.05*r.length;o++)e[0].push(r[o]);for(;o<.2*r.length;o++)e[1].push(r[o]);for(;o<.6*r.length;o++)e[2].push(r[o]);for(;o<r.length;o++)e[3].push(r[o]);console.log("Cache check for: ",e);const i=[];let n=this.cacheGetObjects(e[0]);for(let r=0;r<4;r++){const t=await n;r<3&&(n=this.cacheGetObjects(e[r+1]));const o=Object.keys(t).sort(((e,r)=>s.__closure[e]-s.__closure[r]));for(const e of o)yield"".concat(e,"\t").concat(t[e]);const a=e[r].filter((e=>!(e in t)));i.push(...a)}if(0===i.length)return;if(i.length<=50)t.push(i);else{for(t=[[],[],[],[]],o=0;o<.05*i.length;o++)t[0].push(i[o]);for(;o<.2*i.length;o++)t[1].push(i[o]);for(;o<.6*i.length;o++)t[2].push(i[o]);for(;o<i.length;o++)t[3].push(i[o])}}else{const e=await this.cacheGetObjects(r),o=Object.keys(e).sort(((e,r)=>s.__closure[e]-s.__closure[r]));for(const s of o)yield"".concat(s,"\t").concat(e[s]);if(r=r.filter((s=>!(s in e))),0===r.length)return;t.push(r)}const o=[],i=[],n=[],a=[],c=[];for(let e=0;e<t.length;e++)o.push(new TextDecoder),i.push(null),n.push(null),a.push(""),c.push(!1),this.fetch(this.requestUrlChildren,{method:"POST",headers:{...this.headers,"Content-Type":"application/json"},body:JSON.stringify({objects:JSON.stringify(t[e])})}).then((s=>{const r=s.body.getReader();i[e]=r;const t=r.read().then((s=>(s.reqId=e,s)));n[e]=t}));for(;;){const e=n.filter((e=>!!e));if(0===e.length){if(c.every((e=>e)))break;await new Promise((e=>{setTimeout(e,10)}));continue}const s=await Promise.any(e);let{value:r,done:t,reqId:u}=s;if(c[u]=t,t)a[u].length>0&&(yield a[u],a[u]=""),n[u]=null;else{const e=i[u].read().then((e=>(e.reqId=u,e)));n[u]=e}if(!r)continue;r=o[u].decode(r);const l=(a[u]+r).split(/\r\n|\n|\r/),d=l.pop();a[u]=d;for(const e of l)yield e;this.cacheStoreObjects(l)}}async getRawRootObject(){const e=await this.cacheGetObjects([this.objectId]);if(e[this.objectId])return e[this.objectId];const s=await this.fetch(this.requestUrlRootObj,{headers:this.headers}),r=await s.text();return this.cacheStoreObjects(["".concat(this.objectId,"\t").concat(r)]),r}promisifyIdbRequest(e){return new Promise(((s,r)=>{e.oncomplete=e.onsuccess=()=>s(e.result),e.onabort=e.onerror=()=>r(e.error)}))}async cacheGetObjects(e){if(!this.options.enableCaching||!window.indexedDB)return{};const s={};for(let r=0;r<e.length;r+=500){const t=e.slice(r,r+500),o=this.cacheDB.transaction("objects","readonly").objectStore("objects"),i=t.map((e=>this.promisifyIdbRequest(o.get(e)).then((s=>({id:e,data:s}))))),n=await Promise.all(i);for(const e of n)e.data&&(s[e.id]=e.data)}return s}cacheStoreObjects(e){if(!this.options.enableCaching||!window.indexedDB)return{};const s=this.cacheDB.transaction("objects","readwrite").objectStore("objects");for(const r of e){const e=r.split("\t");s.put(e[1],e[0])}return this.promisifyIdbRequest(s.transaction)}};
1
+ "use strict";require("core-js/modules/es.symbol.description.js"),require("core-js/modules/es.array.flat.js"),require("core-js/modules/es.array.flat-map.js"),require("core-js/modules/es.array.includes.js"),require("core-js/modules/es.array.reduce.js"),require("core-js/modules/es.array.reduce-right.js"),require("core-js/modules/es.array.sort.js"),require("core-js/modules/es.array.unscopables.flat.js"),require("core-js/modules/es.array.unscopables.flat-map.js"),require("core-js/modules/es.math.hypot.js"),require("core-js/modules/es.object.from-entries.js"),require("core-js/modules/es.promise.js"),require("core-js/modules/es.promise.finally.js"),require("core-js/modules/es.regexp.constructor.js"),require("core-js/modules/es.regexp.exec.js"),require("core-js/modules/es.regexp.flags.js"),require("core-js/modules/es.string.replace.js"),require("core-js/modules/es.typed-array.float32-array.js"),require("core-js/modules/es.typed-array.float64-array.js"),require("core-js/modules/es.typed-array.int8-array.js"),require("core-js/modules/es.typed-array.int16-array.js"),require("core-js/modules/es.typed-array.int32-array.js"),require("core-js/modules/es.typed-array.uint8-array.js"),require("core-js/modules/es.typed-array.uint8-clamped-array.js"),require("core-js/modules/es.typed-array.uint16-array.js"),require("core-js/modules/es.typed-array.uint32-array.js"),require("core-js/modules/es.typed-array.from.js"),require("core-js/modules/es.typed-array.of.js"),require("core-js/modules/es.typed-array.set.js"),require("core-js/modules/es.typed-array.sort.js"),require("core-js/modules/esnext.aggregate-error.js"),require("core-js/modules/esnext.array.last-index.js"),require("core-js/modules/esnext.array.last-item.js"),require("core-js/modules/esnext.composite-key.js"),require("core-js/modules/esnext.composite-symbol.js"),require("core-js/modules/esnext.global-this.js"),require("core-js/modules/esnext.map.delete-all.js"),require("core-js/modules/esnext.map.every.js"),require("core-js/modules/esnext.map.filter.js"),require("core-js/modules/esnext.map.find.js"),require("core-js/modules/esnext.map.find-key.js"),require("core-js/modules/esnext.map.from.js"),require("core-js/modules/esnext.map.group-by.js"),require("core-js/modules/esnext.map.includes.js"),require("core-js/modules/esnext.map.key-by.js"),require("core-js/modules/esnext.map.key-of.js"),require("core-js/modules/esnext.map.map-keys.js"),require("core-js/modules/esnext.map.map-values.js"),require("core-js/modules/esnext.map.merge.js"),require("core-js/modules/esnext.map.of.js"),require("core-js/modules/esnext.map.reduce.js"),require("core-js/modules/esnext.map.some.js"),require("core-js/modules/esnext.map.update.js"),require("core-js/modules/esnext.math.clamp.js"),require("core-js/modules/esnext.math.deg-per-rad.js"),require("core-js/modules/esnext.math.degrees.js"),require("core-js/modules/esnext.math.fscale.js"),require("core-js/modules/esnext.math.iaddh.js"),require("core-js/modules/esnext.math.imulh.js"),require("core-js/modules/esnext.math.isubh.js"),require("core-js/modules/esnext.math.rad-per-deg.js"),require("core-js/modules/esnext.math.radians.js"),require("core-js/modules/esnext.math.scale.js"),require("core-js/modules/esnext.math.seeded-prng.js"),require("core-js/modules/esnext.math.signbit.js"),require("core-js/modules/esnext.math.umulh.js"),require("core-js/modules/esnext.number.from-string.js"),require("core-js/modules/esnext.observable.js"),require("core-js/modules/esnext.promise.all-settled.js"),require("core-js/modules/esnext.promise.any.js"),require("core-js/modules/esnext.promise.try.js"),require("core-js/modules/esnext.reflect.define-metadata.js"),require("core-js/modules/esnext.reflect.delete-metadata.js"),require("core-js/modules/esnext.reflect.get-metadata.js"),require("core-js/modules/esnext.reflect.get-metadata-keys.js"),require("core-js/modules/esnext.reflect.get-own-metadata.js"),require("core-js/modules/esnext.reflect.get-own-metadata-keys.js"),require("core-js/modules/esnext.reflect.has-metadata.js"),require("core-js/modules/esnext.reflect.has-own-metadata.js"),require("core-js/modules/esnext.reflect.metadata.js"),require("core-js/modules/esnext.set.add-all.js"),require("core-js/modules/esnext.set.delete-all.js"),require("core-js/modules/esnext.set.difference.js"),require("core-js/modules/esnext.set.every.js"),require("core-js/modules/esnext.set.filter.js"),require("core-js/modules/esnext.set.find.js"),require("core-js/modules/esnext.set.from.js"),require("core-js/modules/esnext.set.intersection.js"),require("core-js/modules/esnext.set.is-disjoint-from.js"),require("core-js/modules/esnext.set.is-subset-of.js"),require("core-js/modules/esnext.set.is-superset-of.js"),require("core-js/modules/esnext.set.join.js"),require("core-js/modules/esnext.set.map.js"),require("core-js/modules/esnext.set.of.js"),require("core-js/modules/esnext.set.reduce.js"),require("core-js/modules/esnext.set.some.js"),require("core-js/modules/esnext.set.symmetric-difference.js"),require("core-js/modules/esnext.set.union.js"),require("core-js/modules/esnext.string.at.js"),require("core-js/modules/esnext.string.code-points.js"),require("core-js/modules/esnext.string.match-all.js"),require("core-js/modules/esnext.string.replace-all.js"),require("core-js/modules/esnext.symbol.dispose.js"),require("core-js/modules/esnext.symbol.observable.js"),require("core-js/modules/esnext.symbol.pattern-match.js"),require("core-js/modules/esnext.weak-map.delete-all.js"),require("core-js/modules/esnext.weak-map.from.js"),require("core-js/modules/esnext.weak-map.of.js"),require("core-js/modules/esnext.weak-set.add-all.js"),require("core-js/modules/esnext.weak-set.delete-all.js"),require("core-js/modules/esnext.weak-set.from.js"),require("core-js/modules/esnext.weak-set.of.js"),require("core-js/modules/web.dom-collections.iterator.js"),require("core-js/modules/web.immediate.js"),require("core-js/modules/web.queue-microtask.js"),require("core-js/modules/web.url.js"),require("core-js/modules/web.url.to-json.js"),require("core-js/modules/web.url-search-params.js");var e=require("@speckle/shared");function s(e,s,r){return s in e?Object.defineProperty(e,s,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[s]=r,e}class r extends Error{constructor(e,s){e||(e=new.target.defaultMessage),super(e,s)}}s(r,"defaultMessage","Unexpected error occurred");class t extends r{}s(t,"defaultMessage","Object loader configured incorrectly!");class o extends r{}s(o,"defaultMessage","Object loader encountered a runtime problem!");module.exports=class{constructor(s){var r;let{serverUrl:i,streamId:a,token:n,objectId:c,options:u={enableCaching:!0,fullyTraverseArrays:!1,excludeProps:[],fetch:null,customLogger:void 0,customWarner:void 0}}=s;if(this.logger=u.customLogger||console.log,this.warner=u.customWarner||console.warn,this.INTERVAL_MS=20,this.TIMEOUT_MS=18e4,this.serverUrl=i||(null===globalThis||void 0===globalThis||null===(r=globalThis.location)||void 0===r?void 0:r.origin),!this.serverUrl)throw new t("Invalid serverUrl specified!");if(this.streamId=a,this.objectId=c,!this.streamId)throw new t("Invalid streamId specified!");if(!this.objectId)throw new t("Invalid objectId specified!");this.logger("Object loader constructor called!");try{this.token=n||e.SafeLocalStorage.get("AuthToken")}catch(e){}this.headers={Accept:"text/plain"},this.token&&(this.headers.Authorization="Bearer ".concat(this.token)),this.requestUrlRootObj="".concat(this.serverUrl,"/objects/").concat(this.streamId,"/").concat(this.objectId,"/single"),this.requestUrlChildren="".concat(this.serverUrl,"/api/getobjects/").concat(this.streamId),this.promises=[],this.intervals={},this.buffer=[],this.isLoading=!1,this.totalChildrenCount=0,this.traversedReferencesCount=0,this.options=u,this.options.numConnections=this.options.numConnections||4,this.cacheDB=null,this.lastAsyncPause=Date.now(),this.existingAsyncPause=null,this.preferredFetch=u.fetch,this.fetch=function(){const e=this.preferredFetch||fetch;if(!e)throw new o("Couldn't find fetch implementation! If running in a node environment, make sure you pass it in through the constructor!");return e(...arguments)}}async asyncPause(){Date.now()-this.lastAsyncPause>=100&&(this.lastAsyncPause=Date.now(),this.existingAsyncPause=new Promise((e=>setTimeout(e,0))),await this.existingAsyncPause,this.existingAsyncPause=null,Date.now()-this.lastAsyncPause>500&&this.logger("Loader Event loop lag: ",Date.now()-this.lastAsyncPause))}dispose(){this.buffer=[],this.intervals.forEach((e=>clearInterval(e.interval)))}async getAndConstructObject(e){await this.downloadObjectsInBuffer(e);const s=await this.getObject(this.objectId);return this.traverseAndConstruct(s,e)}async downloadObjectsInBuffer(e){let s=!0,r=0;for await(const t of this.getObjectIterator())s&&(this.totalChildrenCount=t.totalChildrenCount,s=!1,this.isLoading=!0),r++,e&&e({stage:"download",current:r,total:this.totalChildrenCount});this.isLoading=!1}async traverseAndConstruct(e,s){if(e){if("object"!=typeof e)return e;if(Array.isArray(e)&&0!==e.length){var r,t;const o=[];for(const r of e){if("object"!=typeof r&&!this.options.fullyTraverseArrays)return e;const t=r.referencedId?await this.getObject(r.referencedId):r;r.referencedId&&s&&s({stage:"construction",current:++this.traversedReferencesCount>this.totalChildrenCount?this.totalChildrenCount:this.traversedReferencesCount,total:this.totalChildrenCount}),o.push(await this.traverseAndConstruct(t,s))}return null!==(r=o[0])&&void 0!==r&&null!==(t=r.speckle_type)&&void 0!==t&&t.toLowerCase().includes("datachunk")?o.reduce(((e,s)=>e.concat(s.data)),[]):o}for(const s of this.options.excludeProps)delete e[s];for(const r in e)"object"==typeof e[r]&&null!==e[r]&&(e[r].referencedId&&(e[r]=await this.getObject(e[r].referencedId),s&&s({stage:"construction",current:++this.traversedReferencesCount>this.totalChildrenCount?this.totalChildrenCount:this.traversedReferencesCount,total:this.totalChildrenCount})),e[r]=await this.traverseAndConstruct(e[r],s));return e}}async getObject(e){if(this.buffer[e])return this.buffer[e];return new Promise(((s,r)=>{if(this.promises.push({id:e,resolve:s,reject:r}),this.intervals[e])this.intervals[e].elapsed=0;else{const s=setInterval(this.tryResolvePromise.bind(this),this.INTERVAL_MS,e);this.intervals[e]={interval:s,elapsed:0}}}))}tryResolvePromise(e){if(this.intervals[e].elapsed+=this.INTERVAL_MS,this.buffer[e]){for(const s of this.promises.filter((s=>s.id===e)))s.resolve(this.buffer[e]);return clearInterval(this.intervals[e].interval),void delete this.intervals[e]}this.intervals[e].elapsed>this.TIMEOUT_MS&&(this.warner("Timeout resolving ".concat(e,". HIC SVNT DRACONES.")),clearInterval(this.intervals[e].interval),this.promises.filter((s=>s.id===e)).forEach((e=>e.reject())),this.promises=this.promises.filter((e=>e.id!=e.id)))}async*getObjectIterator(){const e=Date.now();let s=0;for await(const e of this.getRawObjectIterator()){const{id:r,obj:t}=this.processLine(e);this.buffer[r]=t,s+=1,yield t}this.logger("Loaded ".concat(s," objects in: ").concat((Date.now()-e)/1e3))}processLine(e){const s=e.split("\t");return{id:s[0],obj:JSON.parse(s[1])}}supportsCache(){return!(!this.options.enableCaching||!globalThis.indexedDB)}async setupCacheDb(){if(!this.supportsCache()||null!==this.cacheDB)return;await function(){if(navigator.userAgentData||!/Safari\//.test(navigator.userAgent)||/Chrom(e|ium)\//.test(navigator.userAgent)||!indexedDB.databases)return Promise.resolve();let e;return new Promise((s=>{const r=()=>indexedDB.databases().finally(s);e=setInterval(r,100),r()})).finally((()=>clearInterval(e)))}();const e=indexedDB.open("speckle-object-cache",1);e.onupgradeneeded=()=>e.result.createObjectStore("objects"),this.cacheDB=await this.promisifyIdbRequest(e)}async*getRawObjectIterator(){await this.setupCacheDb();const e=await this.getRawRootObject();yield"".concat(this.objectId,"\t").concat(e);const s=JSON.parse(e);if(!s.__closure)return;let r=Object.keys(s.__closure).filter((e=>!e.includes("blob"))).sort(((e,r)=>s.__closure[e]-s.__closure[r]));if(0===r.length)return;let t=[];if(r.length>50){const e=[[],[],[],[]];let o=0;for(;o<.05*r.length;o++)e[0].push(r[o]);for(;o<.2*r.length;o++)e[1].push(r[o]);for(;o<.6*r.length;o++)e[2].push(r[o]);for(;o<r.length;o++)e[3].push(r[o]);this.logger("Cache check for: ",e);const i=[];let a=this.cacheGetObjects(e[0]);for(let r=0;r<4;r++){const t=await a;r<3&&(a=this.cacheGetObjects(e[r+1]));const o=Object.keys(t).sort(((e,r)=>s.__closure[e]-s.__closure[r]));for(const e of o)yield"".concat(e,"\t").concat(t[e]);const n=e[r].filter((e=>!(e in t)));i.push(...n)}if(0===i.length)return;if(i.length<=50)t.push(i);else{for(t=[[],[],[],[]],o=0;o<.05*i.length;o++)t[0].push(i[o]);for(;o<.2*i.length;o++)t[1].push(i[o]);for(;o<.6*i.length;o++)t[2].push(i[o]);for(;o<i.length;o++)t[3].push(i[o])}}else{const e=await this.cacheGetObjects(r),o=Object.keys(e).sort(((e,r)=>s.__closure[e]-s.__closure[r]));for(const s of o)yield"".concat(s,"\t").concat(e[s]);if(r=r.filter((s=>!(s in e))),0===r.length)return;t.push(r)}const o=[],i=[],a=[],n=[],c=[];for(let e=0;e<t.length;e++)o.push(new TextDecoder),i.push(null),a.push(null),n.push(""),c.push(!1),this.fetch(this.requestUrlChildren,{method:"POST",headers:{...this.headers,"Content-Type":"application/json"},body:JSON.stringify({objects:JSON.stringify(t[e])})}).then((s=>{s.body.getReader&&(s.body.iterator=async function*(){const e=this.getReader();for(;;){const s=await e.read();if(s.done)return s.value;yield s.value}});const r=s.body.iterator();i[e]=r;const t=r.next().then((s=>(s.reqId=e,s)));a[e]=t}));for(;;){const e=a.filter((e=>!!e));if(0===e.length){if(c.every((e=>e)))break;await new Promise((e=>{setTimeout(e,10)}));continue}const s=await Promise.any(e);let{value:r,done:t,reqId:u}=s;if(c[u]=t,t)n[u].length>0&&(yield n[u],n[u]=""),a[u]=null;else{const e=i[u].next().then((e=>(e.reqId=u,e)));a[u]=e}if(!r)continue;r=o[u].decode(r);const l=(n[u]+r).split(/\r\n|\n|\r/),d=l.pop();n[u]=d;for(const e of l)yield e;this.cacheStoreObjects(l)}}async getRawRootObject(){const e=await this.cacheGetObjects([this.objectId]);if(e[this.objectId])return e[this.objectId];const s=await this.fetch(this.requestUrlRootObj,{headers:this.headers}),r=await s.text();return this.cacheStoreObjects(["".concat(this.objectId,"\t").concat(r)]),r}promisifyIdbRequest(e){return new Promise(((s,r)=>{e.oncomplete=e.onsuccess=()=>s(e.result),e.onabort=e.onerror=()=>r(e.error)}))}async cacheGetObjects(e){if(!this.supportsCache())return{};const s={};for(let r=0;r<e.length;r+=500){const t=e.slice(r,r+500),o=this.cacheDB.transaction("objects","readonly").objectStore("objects"),i=t.map((e=>this.promisifyIdbRequest(o.get(e)).then((s=>({id:e,data:s}))))),a=await Promise.all(i);for(const e of a)e.data&&(s[e.id]=e.data)}return s}cacheStoreObjects(e){if(!this.supportsCache())return{};const s=this.cacheDB.transaction("objects","readwrite").objectStore("objects");for(const r of e){const e=r.split("\t");s.put(e[1],e[0])}return this.promisifyIdbRequest(s.transaction)}};
@@ -1,5 +1,7 @@
1
- // Since Node v<18 does not provide fetch, we need to pass it in the options object. Note that fetch must return a WHATWG compliant stream, so cross-fetch won't work, but node/undici's implementation will.
2
- import { fetch } from 'undici'
1
+ /**
2
+ * Since Node v<18 does not provide fetch, we need to pass it in the options object. Popular fetch implementations like cross-fetch or node-fetch should work
3
+ */
4
+ import fetch from 'cross-fetch'
3
5
  import ObjectLoader from '../../dist/objectloader.js'
4
6
 
5
7
  const loader = new ObjectLoader({
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@speckle/objectloader",
3
- "version": "2.9.0",
3
+ "version": "2.9.1",
4
4
  "description": "Simple API helper to stream in objects from the Speckle Server.",
5
5
  "main": "dist/objectloader.js",
6
6
  "module": "dist/objectloader.esm.js",
7
+ "types": "types/index.d.ts",
7
8
  "homepage": "https://speckle.systems",
8
9
  "repository": {
9
10
  "type": "git",
@@ -30,6 +31,7 @@
30
31
  "license": "Apache-2.0",
31
32
  "dependencies": {
32
33
  "@babel/core": "^7.17.9",
34
+ "@speckle/shared": "^2.9.1",
33
35
  "core-js": "^3.21.1",
34
36
  "regenerator-runtime": "^0.13.7"
35
37
  },
@@ -40,14 +42,14 @@
40
42
  "@rollup/plugin-babel": "^5.3.1",
41
43
  "@rollup/plugin-commonjs": "^21.0.3",
42
44
  "@rollup/plugin-node-resolve": "^13.1.3",
45
+ "cross-fetch": "^3.1.5",
43
46
  "eslint": "^8.11.0",
44
47
  "eslint-config-prettier": "^8.5.0",
45
48
  "http-server": "^14.1.0",
46
49
  "prettier": "^2.5.1",
47
50
  "rollup": "^2.70.1",
48
51
  "rollup-plugin-delete": "^2.0.0",
49
- "rollup-plugin-terser": "^7.0.2",
50
- "undici": "^4.14.1"
52
+ "rollup-plugin-terser": "^7.0.2"
51
53
  },
52
54
  "gitHead": "5627e490f9a3ecadf19cc4686ad15f344d9ad2d3"
53
55
  }
package/readme.md CHANGED
@@ -64,11 +64,11 @@ let obj = await loader.getAndConstructObject((e) => console.log('Progress', e))
64
64
 
65
65
  ### On the server
66
66
 
67
- Since Node.js does not yet support the [`fetch API`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch), you'll need to provide your own `fetch` function in the options object. Note that `fetch` must return a [Web Stream](https://nodejs.org/api/webstreams.html), so [node-fetch](https://github.com/node-fetch/node-fetch) won't work, but [node/undici's](https://undici.nodejs.org/) implementation will.
67
+ Since Node.js does not yet support the [`fetch API`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch), you'll need to provide your own `fetch` function in the options object, e.g., from `node-fetch` or `cross-fetch`
68
68
 
69
69
  ```js
70
70
  import ObjectLoader from '@speckle/objectloader'
71
- import { fetch } from 'undici'
71
+ import fetch from 'cross-fetch'
72
72
 
73
73
  let loader = new ObjectLoader({
74
74
  serverUrl: 'https://latest.speckle.dev',
@@ -83,6 +83,12 @@ let loader = new ObjectLoader({
83
83
  Run `yarn build` to build prod release, run `yarn build:dev` to build dev release.
84
84
  Or run `yarn dev` to run the build in `watch` mode.
85
85
 
86
+ ### TS types
87
+
88
+ The library isn't written in TypeScript so there's no typing information to be generated out of the box, but since we do want this library to be usable in TypeScript projects we write the types ourselves (for now).
89
+
90
+ So whenever you make any changes to the API, make sure the types file in `types/index.d.ts` is updated
91
+
86
92
  ## Community
87
93
 
88
94
  If in trouble, the Speckle Community hangs out on [the forum](https://speckle.community). Do join and introduce yourself! We're happy to help.
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Base ObjectLoader error
3
+ */
4
+ class BaseError extends Error {
5
+ /**
6
+ * Default message if none is passed
7
+ */
8
+ static defaultMessage = 'Unexpected error occurred'
9
+
10
+ /**
11
+ * @param {string} [message]
12
+ * @param {ErrorOptions} options
13
+ */
14
+ constructor(message, options) {
15
+ message ||= new.target.defaultMessage
16
+ super(message, options)
17
+ }
18
+ }
19
+
20
+ export class ObjectLoaderConfigurationError extends BaseError {
21
+ static defaultMessage = 'Object loader configured incorrectly!'
22
+ }
23
+
24
+ export class ObjectLoaderRuntimeError extends BaseError {
25
+ static defaultMessage = 'Object loader encountered a runtime problem!'
26
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * This adjusts a browser ReadableStream to make it work similarly to Node streams, which further enables us to use the
3
+ * same code to read both kinds of streams. We don't mutate the ReadableStream prototype cause this specific polyfill
4
+ * might not work well in other circumstances (https://github.com/node-fetch/node-fetch/issues/387#issuecomment-417433509)
5
+ *
6
+ * See more: https://github.com/node-fetch/node-fetch/issues/754#issuecomment-602184022
7
+ * @param {ReadableStream} stream
8
+ */
9
+ export function polyfillReadableStreamForAsyncIterator(stream) {
10
+ stream.iterator = async function* () {
11
+ const reader = this.getReader()
12
+ while (1) {
13
+ const chunk = await reader.read()
14
+ if (chunk.done) return chunk.value
15
+ yield chunk.value
16
+ }
17
+ }
18
+ }
package/src/index.js CHANGED
@@ -2,6 +2,13 @@
2
2
  import 'core-js'
3
3
  import 'regenerator-runtime/runtime'
4
4
 
5
+ import { SafeLocalStorage } from '@speckle/shared'
6
+ import {
7
+ ObjectLoaderConfigurationError,
8
+ ObjectLoaderRuntimeError
9
+ } from './errors/index.js'
10
+ import { polyfillReadableStreamForAsyncIterator } from './helpers/stream.js'
11
+
5
12
  /**
6
13
  * Simple client that streams object info from a Speckle Server.
7
14
  * TODO: Object construction progress reporting is weird.
@@ -21,18 +28,34 @@ export default class ObjectLoader {
21
28
  enableCaching: true,
22
29
  fullyTraverseArrays: false,
23
30
  excludeProps: [],
24
- fetch: null
31
+ fetch: null,
32
+ customLogger: undefined,
33
+ customWarner: undefined
25
34
  }
26
35
  }) {
36
+ this.logger = options.customLogger || console.log
37
+ this.warner = options.customWarner || console.warn
38
+
27
39
  this.INTERVAL_MS = 20
28
40
  this.TIMEOUT_MS = 180000 // three mins
29
41
 
30
- this.serverUrl = serverUrl || window.location.origin
42
+ this.serverUrl = serverUrl || globalThis?.location?.origin
43
+ if (!this.serverUrl) {
44
+ throw new ObjectLoaderConfigurationError('Invalid serverUrl specified!')
45
+ }
46
+
31
47
  this.streamId = streamId
32
48
  this.objectId = objectId
33
- console.log('Object loader constructor called!')
49
+ if (!this.streamId) {
50
+ throw new ObjectLoaderConfigurationError('Invalid streamId specified!')
51
+ }
52
+ if (!this.objectId) {
53
+ throw new ObjectLoaderConfigurationError('Invalid objectId specified!')
54
+ }
55
+
56
+ this.logger('Object loader constructor called!')
34
57
  try {
35
- this.token = token || localStorage.getItem('AuthToken')
58
+ this.token = token || SafeLocalStorage.get('AuthToken')
36
59
  } catch (error) {
37
60
  // Accessing localStorage may throw when executing on sandboxed document, ignore.
38
61
  }
@@ -56,6 +79,7 @@ export default class ObjectLoader {
56
79
  this.options = options
57
80
  this.options.numConnections = this.options.numConnections || 4
58
81
 
82
+ /** @type {IDBDatabase | null} */
59
83
  this.cacheDB = null
60
84
 
61
85
  this.lastAsyncPause = Date.now()
@@ -64,8 +88,16 @@ export default class ObjectLoader {
64
88
  // we can't simply bind fetch to this.fetch, so instead we have to do some acrobatics:
65
89
  // https://stackoverflow.com/questions/69337187/uncaught-in-promise-typeerror-failed-to-execute-fetch-on-workerglobalscope#comment124731316_69337187
66
90
  this.preferredFetch = options.fetch
91
+
92
+ /** @type {globalThis.fetch} */
67
93
  this.fetch = function (...args) {
68
94
  const currentFetch = this.preferredFetch || fetch
95
+ if (!currentFetch) {
96
+ throw new ObjectLoaderRuntimeError(
97
+ "Couldn't find fetch implementation! If running in a node environment, make sure you pass it in through the constructor!"
98
+ )
99
+ }
100
+
69
101
  return currentFetch(...args)
70
102
  }
71
103
  }
@@ -81,7 +113,7 @@ export default class ObjectLoader {
81
113
  await this.existingAsyncPause
82
114
  this.existingAsyncPause = null
83
115
  if (Date.now() - this.lastAsyncPause > 500)
84
- console.log('Loader Event loop lag: ', Date.now() - this.lastAsyncPause)
116
+ this.logger('Loader Event loop lag: ', Date.now() - this.lastAsyncPause)
85
117
  }
86
118
  }
87
119
 
@@ -237,7 +269,7 @@ export default class ObjectLoader {
237
269
  }
238
270
 
239
271
  if (this.intervals[id].elapsed > this.TIMEOUT_MS) {
240
- console.warn(`Timeout resolving ${id}. HIC SVNT DRACONES.`)
272
+ this.warner(`Timeout resolving ${id}. HIC SVNT DRACONES.`)
241
273
  clearInterval(this.intervals[id].interval)
242
274
  this.promises.filter((p) => p.id === id).forEach((p) => p.reject())
243
275
  this.promises = this.promises.filter((p) => p.id !== p.id) // clear out
@@ -253,7 +285,7 @@ export default class ObjectLoader {
253
285
  count += 1
254
286
  yield obj
255
287
  }
256
- console.log(`Loaded ${count} objects in: ${(Date.now() - t0) / 1000}`)
288
+ this.logger(`Loaded ${count} objects in: ${(Date.now() - t0) / 1000}`)
257
289
  }
258
290
 
259
291
  processLine(chunk) {
@@ -261,26 +293,36 @@ export default class ObjectLoader {
261
293
  return { id: pieces[0], obj: JSON.parse(pieces[1]) }
262
294
  }
263
295
 
296
+ supportsCache() {
297
+ return !!(this.options.enableCaching && globalThis.indexedDB)
298
+ }
299
+
300
+ async setupCacheDb() {
301
+ if (!this.supportsCache() || this.cacheDB !== null) return
302
+
303
+ // Initialize
304
+ await safariFix()
305
+ const idbOpenRequest = indexedDB.open('speckle-object-cache', 1)
306
+ idbOpenRequest.onupgradeneeded = () =>
307
+ idbOpenRequest.result.createObjectStore('objects')
308
+ this.cacheDB = await this.promisifyIdbRequest(idbOpenRequest)
309
+ }
310
+
264
311
  async *getRawObjectIterator() {
265
- if (this.options.enableCaching && window.indexedDB && this.cacheDB === null) {
266
- await safariFix()
267
- const idbOpenRequest = indexedDB.open('speckle-object-cache', 1)
268
- idbOpenRequest.onupgradeneeded = () =>
269
- idbOpenRequest.result.createObjectStore('objects')
270
- this.cacheDB = await this.promisifyIdbRequest(idbOpenRequest)
271
- }
312
+ await this.setupCacheDb()
272
313
 
273
314
  const rootObjJson = await this.getRawRootObject()
274
- // console.log("Root in: ", Date.now() - tSTART)
315
+ // this.logger("Root in: ", Date.now() - tSTART)
275
316
 
276
317
  yield `${this.objectId}\t${rootObjJson}`
277
318
 
278
319
  const rootObj = JSON.parse(rootObjJson)
279
320
  if (!rootObj.__closure) return
280
321
 
281
- let childrenIds = Object.keys(rootObj.__closure).sort(
282
- (a, b) => rootObj.__closure[a] - rootObj.__closure[b]
283
- )
322
+ let childrenIds = Object.keys(rootObj.__closure)
323
+ .filter((id) => !id.includes('blob'))
324
+ .sort((a, b) => rootObj.__closure[a] - rootObj.__closure[b])
325
+
284
326
  if (childrenIds.length === 0) return
285
327
 
286
328
  let splitHttpRequests = []
@@ -303,7 +345,7 @@ export default class ObjectLoader {
303
345
  splitBeforeCacheCheck[3].push(childrenIds[crtChildIndex])
304
346
  }
305
347
 
306
- console.log('Cache check for: ', splitBeforeCacheCheck)
348
+ this.logger('Cache check for: ', splitBeforeCacheCheck)
307
349
 
308
350
  const newChildren = []
309
351
  let nextCachePromise = this.cacheGetObjects(splitBeforeCacheCheck[0])
@@ -385,9 +427,15 @@ export default class ObjectLoader {
385
427
  headers: { ...this.headers, 'Content-Type': 'application/json' },
386
428
  body: JSON.stringify({ objects: JSON.stringify(splitHttpRequests[i]) })
387
429
  }).then((crtResponse) => {
388
- const crtReader = crtResponse.body.getReader()
430
+ // Polyfill web streams so that we can work with them the same way we do in Node
431
+ if (crtResponse.body.getReader) {
432
+ polyfillReadableStreamForAsyncIterator(crtResponse.body)
433
+ }
434
+
435
+ // Get stream async iterator
436
+ const crtReader = crtResponse.body.iterator()
389
437
  readers[i] = crtReader
390
- const crtReadPromise = crtReader.read().then((x) => {
438
+ const crtReadPromise = crtReader.next().then((x) => {
391
439
  x.reqId = i
392
440
  return x
393
441
  })
@@ -417,7 +465,7 @@ export default class ObjectLoader {
417
465
 
418
466
  // Replace read promise on this request with a new `read` call
419
467
  if (!readerDone) {
420
- const crtReadPromise = readers[reqId].read().then((x) => {
468
+ const crtReadPromise = readers[reqId].next().then((x) => {
421
469
  x.reqId = reqId
422
470
  return x
423
471
  })
@@ -464,7 +512,7 @@ export default class ObjectLoader {
464
512
  }
465
513
 
466
514
  async cacheGetObjects(ids) {
467
- if (!this.options.enableCaching || !window.indexedDB) {
515
+ if (!this.supportsCache()) {
468
516
  return {}
469
517
  }
470
518
 
@@ -481,7 +529,7 @@ export default class ObjectLoader {
481
529
  )
482
530
  const cachedData = await Promise.all(idbChildrenPromises)
483
531
 
484
- // console.log("Cache check for : ", idsChunk.length, Date.now() - t0)
532
+ // this.logger("Cache check for : ", idsChunk.length, Date.now() - t0)
485
533
 
486
534
  for (const cachedObj of cachedData) {
487
535
  if (!cachedObj.data)
@@ -495,7 +543,7 @@ export default class ObjectLoader {
495
543
  }
496
544
 
497
545
  cacheStoreObjects(objects) {
498
- if (!this.options.enableCaching || !window.indexedDB) {
546
+ if (!this.supportsCache()) {
499
547
  return {}
500
548
  }
501
549
 
@@ -511,7 +559,10 @@ export default class ObjectLoader {
511
559
  }
512
560
  }
513
561
 
514
- // Credits and more info: https://github.com/jakearchibald/safari-14-idb-fix
562
+ /**
563
+ * Fixes a Safari bug where IndexedDB requests get lost and never resolve - invoke before you use IndexedDB
564
+ * @link Credits and more info: https://github.com/jakearchibald/safari-14-idb-fix
565
+ */
515
566
  function safariFix() {
516
567
  const isSafari =
517
568
  !navigator.userAgentData &&
@@ -0,0 +1,49 @@
1
+ /**
2
+ * This is written manually & should be kept up to date when the API changes
3
+ */
4
+
5
+ export interface SpeckleObject extends Record<string, unknown> {
6
+ totalChildrenCount?: number
7
+ }
8
+
9
+ type Logger = (...args: unknown[]) => void
10
+
11
+ export type ProgressStage = 'download' | 'construction'
12
+
13
+ /**
14
+ * ObjectLoader class
15
+ */
16
+ export default class ObjectLoader {
17
+ constructor(params: {
18
+ serverUrl: string
19
+ streamId: string
20
+ objectId: string
21
+ token?: string
22
+ options?: Partial<{
23
+ /**
24
+ * Whether IndexedDB caching is enabled (disabled by default in node envs where IndexedDB is not available)
25
+ */
26
+ enableCaching: boolean
27
+ fullyTraverseArrays: boolean
28
+ excludeProps: Array
29
+ /**
30
+ * Override fetch implementation (necessary in node environment)
31
+ */
32
+ fetch: GlobalFetch['fetch']
33
+ /**
34
+ * Optionally provide alternative for console.log
35
+ */
36
+ customLogger: Logger
37
+ /**
38
+ * Optionally provide alternative for console.warn
39
+ */
40
+ customWarner: Logger
41
+ }>
42
+ })
43
+
44
+ async getAndConstructObject(
45
+ onProgress: (e: { stage: ProgressStage; current: number; total: number }) => void
46
+ ): SpeckleObject | SpeckleObject[]
47
+
48
+ async *getObjectIterator(): Generator<SpeckleObject, SpeckleObject>
49
+ }