@speckle/objectloader2
Version:
This is an updated objectloader for the Speckle viewer written in typescript
163 lines • 5.95 kB
JavaScript
import BatchedPool from '../../helpers/batchedPool.js';
import { ObjectLoaderRuntimeError } from '../../types/errors.js';
import { isBase, take } from '../../types/types.js';
export default class ServerDownloader {
constructor(options) {
this.
this.
options.fetch ?? ((...args) => globalThis.fetch(...args));
this.
if (options.headers) {
for (const header of options.headers.entries()) {
this.
}
}
this.
if (this.
this.
}
this.
this.
}
if (total <= 50) {
return [total];
}
return [10000, 25000, 10000, 1000];
}
initializePool(params) {
const { results, total } = params;
this.
this.
concurrencyAndSizes: this.
maxWaitTime: params.maxDownloadBatchWait,
processFunction: (batch) => this.downloadBatch({
batch,
url: this.
headers: this.
})
});
}
if (this.
return this.
}
throw new Error('Download pool is not initialized');
}
add(id) {
this.
}
async disposeAsync() {
await this.
}
let base;
try {
base = JSON.parse(unparsedBase);
}
catch (e) {
throw new Error(`Error parsing object ${baseId}: ${e.message}`);
}
if (isBase(base)) {
return { baseId, base };
}
else {
throw new ObjectLoaderRuntimeError(`${baseId} is not a base`);
}
}
async downloadBatch(params) {
const { batch, url, headers } = params;
const keys = new Set(batch);
const response = await this.
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({ objects: JSON.stringify(batch) })
});
this.
if (!response.body) {
throw new Error('ReadableStream not supported or response has no body.');
}
const reader = response.body.getReader();
let leftover = new Uint8Array(0);
let count = 0;
while (true) {
const { done, value } = await reader.read();
if (done)
break;
leftover = await this.processArray(leftover, value, keys, async () => {
count++;
if (count % 1000 === 0) {
await new Promise((resolve) => setTimeout(resolve, 100)); //allow other stuff to happen
}
});
}
if (keys.size > 0) {
throw new Error('Items requested were not downloaded: ' + take(keys.values(), 10).join(','));
}
}
async processArray(leftover, value, keys, callback) {
//this concat will allocate a new array
const combined = this.concatUint8Arrays(leftover, value);
let start = 0;
//subarray doesn't allocate
for (let i = 0; i < combined.length; i++) {
if (combined[i] === 0x0a) {
const line = combined.subarray(start, i); // line without \n
//strings are allocated here
const item = this.processLine(line);
this.
start = i + 1;
await callback();
keys.delete(item.baseId);
}
}
return combined.subarray(start); // carry over remainder
}
processLine(line) {
for (let i = 0; i < line.length; i++) {
if (line[i] === 0x09) {
//this is a tab
const baseId = this.
const json = line.subarray(i + 1);
const base = this.
const item = this.
item.size = json.length;
return item;
}
}
throw new ObjectLoaderRuntimeError('Invalid line format: ' + this.
}
concatUint8Arrays(a, b) {
const c = new Uint8Array(a.length + b.length);
c.set(a, 0);
c.set(b, a.length);
return c;
}
async downloadSingle() {
const response = await this.
headers: this.
});
this.
const responseText = await response.text();
const item = this.
item.size = 0;
return item;
}
if (!response.ok) {
if ([401, 403].includes(response.status)) {
throw new ObjectLoaderRuntimeError('You do not have access!');
}
throw new ObjectLoaderRuntimeError(`Failed to fetch objects: ${response.status} ${response.statusText})`);
}
}
}
//# sourceMappingURL=serverDownloader.js.map