UNPKG

@inweb/client

Version:

JavaScript REST API client for the Open Cloud Server

2,441 lines 87.3 kB
import { EventEmitter2 } from "@inweb/eventemitter2";

class Endpoint {
    constructor(path, httpClient, headers = {}) {
        this.path = path;
        this.httpClient = httpClient;
        this.headers = headers;
    }
    appendVersionParam(relativePath) {
        if (this._useVersion === undefined) return relativePath;
        const delimiter = relativePath.includes("?") ? "&" : "?";
        return `${relativePath}${delimiter}version=${this._useVersion}`;
    }
    getEndpointPath(relativePath) {
        return this.appendVersionParam(`${this.path}${relativePath}`);
    }
    get(relativePath, signal) {
        return this.httpClient.get(this.getEndpointPath(relativePath), {
            signal: signal,
            headers: this.headers
        });
    }
    post(relativePath, body) {
        return this.httpClient.post(this.getEndpointPath(relativePath), body, {
            headers: this.headers
        });
    }
    put(relativePath, body) {
        return this.httpClient.put(this.getEndpointPath(relativePath), body, {
            headers: this.headers
        });
    }
    delete(relativePath) {
        return this.httpClient.delete(this.getEndpointPath(relativePath), {
            headers: this.headers
        });
    }
    useVersion(version) {
        this._useVersion = version;
        return this;
    }
}

const STATUS_CODES = {
    100: "Continue",
    101: "Switching Protocols",
    102: "Processing",
    103: "Early Hints",
    200: "OK",
    201: "Created",
    202: "Accepted",
    203: "Non-Authoritative Information",
    204: "No Content",
    205: "Reset Content",
    206: "Partial Content",
    207: "Multi-Status",
    208: "Already Reported",
    226: "IM Used",
    300: "Multiple Choices",
    301: "Moved Permanently",
    302: "Found",
    303: "See Other",
    304: "Not Modified",
    305: "Use Proxy",
    307: "Temporary Redirect",
    308: "Permanent Redirect",
    400: "Bad Request",
    401: "Unauthorized",
    402: "Payment Required",
    403: "Forbidden",
    404: "Not Found",
    405: "Method Not Allowed",
    406: "Not Acceptable",
    407: "Proxy Authentication Required",
    408: "Request Time-out",
    409: "Conflict",
    410: "Gone",
    411: "Length Required",
    412: "Precondition Failed",
    413: "Payload Too Large",
    414: "URI Too Long",
    415: "Unsupported Media Type",
    416: "Range Not Satisfiable",
    417: "Expectation Failed",
    418: "I'm a teapot",
    421: "Misdirected Request",
    422: "Unprocessable Entity",
    423: "Locked",
    424: "Failed Dependency",
    425: "Too Early",
    426: "Upgrade Required",
    428: "Precondition Required",
    429: "Too Many Requests",
    431: "Header Fields Too Large",
    451: "Unavailable For Legal Reasons",
    500: "Internal Server Error",
    501: "Not Implemented",
    502: "Bad Gateway",
    503: "Service Unavailable",
    504: "Gateway Timeout",
    505: "HTTP Version Not Supported",
    506: "Variant Also Negotiates",
    507: "Insufficient Storage",
    508: "Loop Detected",
    509: "Bandwidth Limit Exceeded",
    510: "Not Extended",
    511: "Network Authentication Required"
};

function statusText(status) {
    return STATUS_CODES[status] || `Error ${status}`;
}

function error400(text, _default = "400") {
    try {
        return JSON.parse(text).description;
    } catch {
        return _default;
    }
}

class FetchError extends Error {
    constructor(status, message) {
        super(message || statusText(status));
        this.name = "FetchError";
        this.status = status;
        this.statusText = statusText(status);
    }
}

class Model extends Endpoint {
    constructor(data, file) {
        super(`${file.path}/downloads`, file.httpClient);
        this._data = data;
        this._file = file;
    }
    get assembly() {
        return this._file;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        this._data = value;
    }
    get database() {
        return this.data.database;
    }
    get default() {
        return this.data.default;
    }
    get file() {
        return this._file;
    }
    get fileId() {
        return this.data.fileId;
    }
    get geometry() {
        return this.data.geometry;
    }
    get id() {
        return this.data.id;
    }
    get name() {
        return this.data.name;
    }
    get type() {
        return this.file.type;
    }
    get version() {
        return this.data.version;
    }
    getModels() {
        return Promise.resolve([ this ]);
    }
    getModelTransformMatrix(handle) {
        return this.file.getModelTransformMatrix(handle);
    }
    setModelTransformMatrix(handle, transform) {
        return this.file.setModelTransformMatrix(handle, transform).then((() => this));
    }
    getViewpoints() {
        return this._file.getViewpoints().then((array => array.filter((({custom_fields: custom_fields = {}}) => custom_fields.modelId === this.id || custom_fields.modelName === this.name))));
    }
    saveViewpoint(viewpoint) {
        return this._file.saveViewpoint({
            ...viewpoint,
            custom_fields: {
                ...viewpoint.custom_fields,
                modelId: this.id,
                modelName: this.name
            }
        });
    }
    deleteViewpoint(guid) {
        return this._file.deleteViewpoint(guid);
    }
    getSnapshot(guid) {
        return this._file.getSnapshot(guid);
    }
    getSnapshotData(guid, bitmapGuid) {
        return this._file.getSnapshotData(guid, bitmapGuid);
    }
    downloadResource(dataId, onProgress, signal) {
        return this._file.downloadResource(dataId, onProgress, signal);
    }
    downloadResourceRange(dataId, requestId, ranges, onProgress, signal) {
        return this._file.downloadResourceRange(dataId, requestId, ranges, onProgress, signal);
    }
    partialDownloadResource(dataId, onProgress, signal) {
        console.warn("Model.partialDownloadResource() has been deprecated since 25.3 and will be removed in a future release, use Model.downloadResource() instead.");
        return this.downloadResource(dataId, onProgress, signal);
    }
    async downloadFileRange(requestId, records, dataId, onProgress, signal) {
        if (!records) return;
        let ranges = [];
        if (records.length) {
            ranges = records.map((record => ({
                begin: Number(record.begin),
                end: Number(record.end),
                requestId: record.reqId
            })));
        } else {
            for (let i = 0; i < records.size(); i++) {
                const record = records.get(i);
                ranges.push({
                    begin: Number(record.begin),
                    end: Number(record.end),
                    requestId: requestId
                });
                record.delete();
            }
        }
        await this.downloadResourceRange(dataId, requestId, ranges, onProgress, signal);
    }
    getReferences(signal) {
        return this._file.getReferences(signal);
    }
}

function delay(ms, signal) {
    return new Promise((resolve => {
        let timeoutId = 0;
        const abortHandler = () => {
            clearTimeout(timeoutId);
            resolve(true);
        };
        timeoutId = window.setTimeout((() => {
            signal.removeEventListener("abort", abortHandler);
            resolve(false);
        }), ms);
        signal.addEventListener("abort", abortHandler, {
            once: true
        });
    }));
}

async function waitFor(func, params = {}) {
    var _a, _b, _c;
    const timeout = params.timeout || 6e5;
    const interval = params.interval || 3e3;
    const signal = (_a = params.signal) !== null && _a !== undefined ? _a : (new AbortController).signal;
    const abortError = (_b = params.abortError) !== null && _b !== undefined ? _b : new DOMException("Aborted", "AbortError");
    const timeoutError = (_c = params.timeoutError) !== null && _c !== undefined ? _c : new DOMException("Timeout", "TimeoutError");
    const end = performance.now() + timeout;
    let count = timeout / interval;
    do {
        if (await func(params)) return Promise.resolve(params.result);
        if (await delay(interval, signal) || signal.aborted) return Promise.reject(abortError);
    } while (performance.now() < end && --count > 0);
    return Promise.reject(timeoutError);
}

function parseArgs(args) {
    if (typeof args === "string") {
        const firstArg = args.indexOf("--");
        if (firstArg !== -1) args = args.slice(firstArg);
        const argArray = args.split("--").map((x => x.split("=").map((y => y.split(" "))).flat())).filter((x => x[0])).map((x => x.concat([ "" ])));
        return Object.fromEntries(argArray);
    }
    return args || {};
}

function userFullName(firstName, lastName = "", userName = "") {
    var _a;
    if (firstName && typeof firstName !== "string") {
        return userFullName((_a = firstName.firstName) !== null && _a !== undefined ? _a : firstName.name, firstName.lastName, firstName.userName);
    }
    return `${firstName !== null && firstName !== undefined ? firstName : ""} ${lastName !== null && lastName !== undefined ? lastName : ""}`.trim() || userName;
}

function userInitials(fullName = "") {
    const names = fullName.split(" ").filter((x => x));
    return names.reduce(((initials, name, index) => {
        if (index === 0 || index === names.length - 1) initials += name.charAt(0);
        return initials;
    }), "").toUpperCase();
}

class ClashTest extends Endpoint {
    constructor(data, path, httpClient) {
        super(`${path}/clashes/${data.id}`, httpClient);
        this.data = data;
    }
    get clearance() {
        return this.data.clearance;
    }
    get createdAt() {
        return this.data.createdAt;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        this._data = value;
        this._data.owner.avatarUrl = `${this.httpClient.serverUrl}/users/${this._data.owner.userId}/avatar`;
        this._data.owner.fullName = userFullName(this._data.owner);
        this._data.owner.initials = userInitials(this._data.owner.fullName);
    }
    get id() {
        return this.data.id;
    }
    get lastModifiedAt() {
        return this.data.lastModifiedAt;
    }
    get name() {
        return this.data.name;
    }
    set name(value) {
        this.data.name = value;
    }
    get owner() {
        return this.data.owner;
    }
    get selectionSetA() {
        return this.data.selectionSetA;
    }
    get selectionTypeA() {
        return this.data.selectionTypeA;
    }
    get selectionSetB() {
        return this.data.selectionSetB;
    }
    get selectionTypeB() {
        return this.data.selectionTypeB;
    }
    get status() {
        return this.data.status;
    }
    get tolerance() {
        return this.data.tolerance;
    }
    async checkout() {
        const response = await this.get("");
        this.data = await response.json();
        return this;
    }
    async update(data) {
        const response = await this.put("", data);
        this.data = await response.json();
        return this;
    }
    delete() {
        return super.delete("").then((response => response.json()));
    }
    save() {
        return this.update(this.data);
    }
    waitForDone(params) {
        const checkDone = () => this.checkout().then((test => {
            var _a;
            const ready = [ "done", "failed" ].includes(test.status);
            const cancel = (_a = params === null || params === undefined ? undefined : params.onCheckout) === null || _a === undefined ? undefined : _a.call(params, test, ready);
            return cancel || ready;
        }));
        return waitFor(checkDone, params).then((() => this));
    }
    getReport() {
        return this.get("/report").then((response => response.json()));
    }
}

class Assembly extends Endpoint {
    constructor(data, httpClient) {
        super(`/assemblies/${data.id}`, httpClient);
        this.data = data;
    }
    get activeVersion() {
        return this.data.activeVersion;
    }
    get associatedFiles() {
        return this.data.associatedFiles;
    }
    get created() {
        return this.data.created;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        var _a;
        var _b;
        this._data = value;
        this._data.owner.avatarUrl = `${this.httpClient.serverUrl}/users/${this._data.owner.userId}/avatar`;
        this._data.owner.fullName = userFullName(this._data.owner);
        this._data.owner.initials = userInitials(this._data.owner.fullName);
        (_a = (_b = this._data).associatedFiles) !== null && _a !== undefined ? _a : _b.associatedFiles = [];
        this._data.associatedFiles.forEach((file => file.link = `${this.httpClient.serverUrl}/files/${file.fileId}`));
    }
    get files() {
        return this.data.files;
    }
    get geometryType() {
        return this.status === "done" ? "vsfx" : "";
    }
    get id() {
        return this.data.id;
    }
    get name() {
        return this.data.name;
    }
    set name(value) {
        this.data.name = value;
    }
    get originalAssemblyId() {
        return this.data.originalAssemblyId;
    }
    get owner() {
        return this.data.owner;
    }
    get previewUrl() {
        return this.data.previewUrl || "";
    }
    get relatedJobs() {
        return this.data.relatedJobs;
    }
    get status() {
        return this.data.status;
    }
    get type() {
        return "assembly";
    }
    get version() {
        return this.data.version;
    }
    get versions() {
        return this.data.versions;
    }
    async checkout() {
        const response = await this.get("");
        this.data = await response.json();
        return this;
    }
    async update(data) {
        const response = await this.put("", data);
        this.data = await response.json();
        return this;
    }
    delete() {
        return super.delete("").then((response => response.json()));
    }
    save() {
        return this.update(this.data);
    }
    setPreview(image) {
        console.warn("Assembly does not support preview");
        return Promise.resolve(this);
    }
    deletePreview() {
        console.warn("Assembly does not support preview");
        return Promise.resolve(this);
    }
    getModels() {
        return this.get("/geometry").then((response => response.json())).then((array => array.map((data => new Model(data, this)))));
    }
    getModelTransformMatrix(handle) {
        return this.data.transform[handle];
    }
    setModelTransformMatrix(handle, transform) {
        const obj = {
            ...this.data.transform
        };
        obj[handle] = transform;
        return this.update({
            transform: obj
        });
    }
    getProperties(handles, group = false) {
        const searchParams = new URLSearchParams;
        if (handles) {
            if (Array.isArray(handles)) handles = handles.join(",");
            if (typeof handles === "string") handles = handles.trim();
            if (handles) searchParams.set("handles", handles);
        }
        if (group) searchParams.set("group", "true");
        let queryString = searchParams.toString();
        if (queryString) queryString = "?" + queryString;
        return this.get(`/properties${queryString}`).then((response => response.json()));
    }
    searchProperties(searchPattern) {
        return this.post("/properties/search", searchPattern).then((response => response.json()));
    }
    getCdaTree() {
        return this.get(`/properties/tree`).then((response => response.json()));
    }
    getViewpoints() {
        return this.get("/viewpoints").then((response => response.json())).then((viewpoints => viewpoints.result));
    }
    saveViewpoint(viewpoint) {
        return this.post("/viewpoints", viewpoint).then((response => response.json()));
    }
    deleteViewpoint(guid) {
        return super.delete(`/viewpoints/${guid}`).then((response => response.json()));
    }
    getSnapshot(guid) {
        return this.get(`/viewpoints/${guid}/snapshot`).then((response => response.text()));
    }
    getSnapshotData(guid, bitmapGuid) {
        return this.get(`/viewpoints/${guid}/bitmaps/${bitmapGuid}`).then((response => response.text()));
    }
    downloadResource(dataId, onProgress, signal) {
        return this.httpClient.downloadFile(this.getEndpointPath(`/downloads/${dataId}`), onProgress, {
            signal: signal,
            headers: this.headers
        }).then((response => response.arrayBuffer()));
    }
    downloadResourceRange(dataId, requestId, ranges, onProgress, signal) {
        return this.httpClient.downloadFileRange(this.getEndpointPath(`/downloads/${dataId}${requestId ? "?requestId=" + requestId : ""}`), requestId, ranges, onProgress, {
            signal: signal,
            headers: this.headers
        }).then((response => response.arrayBuffer()));
    }
    partialDownloadResource(dataId, onProgress, signal) {
        console.warn("Assembly.partialDownloadResource() has been deprecated since 25.3 and will be removed in a future release, use Assembly.downloadResource() instead.");
        return this.downloadResource(dataId, onProgress, signal);
    }
    async downloadFileRange(requestId, records, dataId, onProgress, signal) {
        await this.downloadResourceRange(dataId, requestId, records, onProgress, signal);
    }
    async getReferences(signal) {
        const files = new Endpoint("/files", this.httpClient, this.headers);
        const references = await Promise.all(this.associatedFiles.map((file => `/${file.fileId}/references`)).map((link => files.get(link, signal).then((response => response.json()))))).then((references => references.map((x => x.references)))).then((references => references.reduce(((x, v) => [ ...v, ...x ]), []))).then((references => [ ...new Set(references.map(JSON.stringify)) ].map((x => JSON.parse(x)))));
        return {
            id: "",
            references: references
        };
    }
    waitForDone(params) {
        const checkDone = () => this.checkout().then((assembly => {
            var _a;
            const ready = [ "done", "failed" ].includes(assembly.status);
            const cancel = (_a = params === null || params === undefined ? undefined : params.onCheckout) === null || _a === undefined ? undefined : _a.call(params, assembly, ready);
            return cancel || ready;
        }));
        return waitFor(checkDone, params).then((() => this));
    }
    getClashTests(start, limit, name, ids, sortByDesc, sortField) {
        const searchParams = new URLSearchParams;
        if (start > 0) searchParams.set("start", start.toString());
        if (limit > 0) searchParams.set("limit", limit.toString());
        if (name) searchParams.set("name", name);
        if (ids) {
            if (Array.isArray(ids)) ids = ids.join("|");
            if (typeof ids === "string") ids = ids.trim();
            if (ids) searchParams.set("id", ids);
        }
        if (sortByDesc !== undefined) searchParams.set("sortBy", sortByDesc ? "desc" : "asc");
        if (sortField) searchParams.set("sortField", sortField);
        let queryString = searchParams.toString();
        if (queryString) queryString = "?" + queryString;
        return this.get(`/clashes${queryString}`).then((response => response.json())).then((tests => ({
            ...tests,
            result: tests.result.map((data => new ClashTest(data, this.path, this.httpClient)))
        })));
    }
    getClashTest(testId) {
        return this.get(`/clashes/${testId}`).then((response => response.json())).then((data => new ClashTest(data, this.path, this.httpClient)));
    }
    createClashTest(name, selectionTypeA, selectionTypeB, selectionSetA, selectionSetB, params) {
        const {tolerance: tolerance, clearance: clearance, waitForDone: waitForDone} = params !== null && params !== undefined ? params : {};
        if (!Array.isArray(selectionSetA)) selectionSetA = [ selectionSetA ];
        if (!Array.isArray(selectionSetB)) selectionSetB = [ selectionSetB ];
        return this.post("/clashes", {
            name: name,
            selectionTypeA: selectionTypeA,
            selectionTypeB: selectionTypeB,
            selectionSetA: selectionSetA,
            selectionSetB: selectionSetB,
            tolerance: tolerance,
            clearance: clearance
        }).then((response => response.json())).then((data => new ClashTest(data, this.path, this.httpClient))).then((result => waitForDone ? result.waitForDone(params) : result));
    }
    deleteClashTest(testId) {
        return super.delete(`/clashes/${testId}`).then((response => response.json()));
    }
    updateVersion(files, params = {
        waitForDone: false
    }) {
        return Promise.reject(new Error("Assembly version support will be implemeted in a future release"));
    }
    getVersions() {
        return Promise.resolve(undefined);
    }
    getVersion(version) {
        return Promise.reject(new FetchError(404));
    }
    deleteVersion(version) {
        return Promise.reject(new FetchError(404));
    }
    setActiveVersion(version) {
        return this.update({
            activeVersion: version
        });
    }
    createSharedLink(permissions) {
        return Promise.reject(new Error("Assembly shared link will be implemeted in a future release"));
    }
    getSharedLink() {
        return Promise.resolve(undefined);
    }
    deleteSharedLink() {
        return Promise.reject(new FetchError(404));
    }
}

function handleFetchError(response) {
    if (!response.ok) {
        switch (response.status) {
          case 400:
            {
                return response.text().then((text => {
                    console.error(text);
                    return Promise.reject(new FetchError(400, error400(text)));
                }));
            }

          case 500:
            {
                return response.text().then((text => {
                    console.error(error400(text, text));
                    return Promise.reject(new FetchError(500));
                }));
            }

          default:
            return Promise.reject(new FetchError(response.status));
        }
    }
    return Promise.resolve(response);
}

function $fetch(url, init = {
    method: "GET"
}) {
    const headers = {
        ...init.headers
    };
    delete headers["Content-Type"];
    Object.keys(headers).filter((x => headers[x] === undefined)).forEach((x => delete headers[x]));
    let body = undefined;
    if (init.method === "POST" || init.method === "PUT") {
        if (init.body instanceof FormData) {
            body = init.body;
        } else if (init.body instanceof Blob) {
            body = new FormData;
            body.append("file", init.body);
        } else if (init.body instanceof ArrayBuffer) {
            body = new FormData;
            body.append("file", new Blob([ init.body ]));
        } else if (typeof init.body === "object") {
            body = JSON.stringify(init.body);
            headers["Content-Type"] = "application/json";
        } else if (typeof init.body === "string") {
            body = init.body;
            headers["Content-Type"] = "text/plain";
        }
    }
    return fetch(url, {
        ...init,
        headers: headers,
        body: body
    }).then(handleFetchError);
}

function handleXMLHttpError(xhr) {
    if (xhr.status === 0) {
        return Promise.reject(new FetchError(0, "Network error"));
    }
    if (xhr.status < 200 || xhr.status > 299) {
        switch (xhr.status) {
          case 400:
            {
                console.error(xhr.responseText);
                return Promise.reject(new FetchError(400, error400(xhr.responseText)));
            }

          case 500:
            {
                console.error(error400(xhr.responseText, xhr.responseText));
                return Promise.reject(new FetchError(500));
            }

          default:
            {
                return Promise.reject(new FetchError(xhr.status));
            }
        }
    }
    return Promise.resolve(xhr);
}

function $xmlhttp(url, params = {
    method: "GET"
}) {
    return new Promise(((resolve, reject) => {
        const xhr = new XMLHttpRequest;
        xhr.open(params.method, url, true);
        for (const key in params.headers) {
            xhr.setRequestHeader(key, params.headers[key]);
        }
        function calcProgress(event) {
            return event.lengthComputable ? event.loaded / event.total : 1;
        }
        xhr.upload.onprogress = event => params.uploadProgress && params.uploadProgress(calcProgress(event));
        xhr.onprogress = event => params.downloadProgress && params.downloadProgress(calcProgress(event));
        xhr.onloadend = event => handleXMLHttpError(event.target).then(resolve, reject);
        xhr.send(params.body);
    }));
}

class HttpClient {
    constructor(serverUrl) {
        this.headers = {};
        this.signInUserId = "";
        this.signInUserIsAdmin = false;
        this.serverUrl = serverUrl;
    }
    get(relativePath, init = {}) {
        return $fetch(`${this.serverUrl}${relativePath}`, {
            ...init,
            method: "GET",
            headers: {
                ...this.headers,
                ...init.headers
            }
        });
    }
    post(relativePath, body, init = {}) {
        return $fetch(`${this.serverUrl}${relativePath}`, {
            ...init,
            method: "POST",
            headers: {
                ...this.headers,
                ...init.headers
            },
            body: body
        });
    }
    put(relativePath, body, init = {}) {
        return $fetch(`${this.serverUrl}${relativePath}`, {
            ...init,
            method: "PUT",
            headers: {
                ...this.headers,
                ...init.headers
            },
            body: body
        });
    }
    delete(relativePath, init = {}) {
        return $fetch(`${this.serverUrl}${relativePath}`, {
            ...init,
            method: "DELETE",
            headers: {
                ...this.headers,
                ...init.headers
            }
        });
    }
    uploadFile(relativePath, file, onProgress, init = {}) {
        const data = new FormData;
        data.append("file", file);
        return $xmlhttp(`${this.serverUrl}${relativePath}`, {
            method: "POST",
            headers: {
                ...this.headers,
                ...init.headers
            },
            body: data,
            uploadProgress: onProgress
        });
    }
    async downloadFile(relativePath, onProgress, init = {}) {
        const response = await this.get(relativePath, init);
        if (!onProgress) return response;
        const contentLength = response.headers.get("Content-Length");
        const total = parseInt(contentLength || "", 10) || 1;
        const stream = new ReadableStream({
            async start(controller) {
                const reader = response.body.getReader();
                let loaded = 0;
                while (true) {
                    const {done: done, value: value} = await reader.read();
                    if (done) break;
                    controller.enqueue(value);
                    loaded += value.length;
                    onProgress(loaded / total, value);
                }
                controller.close();
            }
        });
        return new Response(stream);
    }
    async downloadFileRange(relativePath, reserved, ranges, onProgress, init = {}) {
        const headers = {
            ...init.headers,
            Range: "bytes=" + ranges.map((x => `${x.begin}-${x.end}`)).join(",")
        };
        const response = await this.get(relativePath, {
            ...init,
            headers: headers
        });
        if (!onProgress) return response;
        const contentLength = response.headers.get("content-length");
        const total = parseInt(contentLength || "", 10) || 1;
        const stream = new ReadableStream({
            async start(controller) {
                const reader = response.body.getReader();
                let loaded = 0;
                let rangedIndex = 0;
                let rangePos = 0;
                while (true) {
                    const {done: done, value: value} = await reader.read();
                    if (done) break;
                    controller.enqueue(value);
                    loaded += value.length;
                    let chunkLeft = value.length;
                    let chunkPos = 0;
                    while (chunkLeft > 0) {
                        const range = ranges[rangedIndex];
                        const rangeLeft = range.end - range.begin + 1 - rangePos;
                        if (chunkLeft < rangeLeft) {
                            const chunk = value.subarray(chunkPos, chunkPos + chunkLeft);
                            onProgress(loaded / total, chunk, range.requestId);
                            rangePos += chunkLeft;
                            chunkLeft = 0;
                        } else {
                            const chunk = value.subarray(chunkPos, chunkPos + rangeLeft);
                            onProgress(loaded / total, chunk, range.requestId);
                            chunkPos += rangeLeft;
                            chunkLeft -= rangeLeft;
                            rangedIndex++;
                            rangePos = 0;
                        }
                    }
                }
                controller.close();
            }
        });
        return new Response(stream);
    }
}

class Permission extends Endpoint {
    constructor(data, fileId, httpClient) {
        super(`/files/${fileId}/permissions/${data.id}`, httpClient);
        this.data = data;
    }
    get actions() {
        return this.data.actions;
    }
    set actions(value) {
        this._data.actions = value;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        this._data = value;
    }
    get id() {
        return this.data.id;
    }
    get grantedTo() {
        return this.data.grantedTo;
    }
    set grantedTo(value) {
        this.data.grantedTo = value;
    }
    get public() {
        return this.data.public;
    }
    set public(value) {
        this.data.public = value;
    }
    async checkout() {
        const response = await this.get("");
        this.data = await response.json();
        return this;
    }
    async update(data) {
        const response = await this.put("", data);
        this.data = await response.json();
        return this;
    }
    delete() {
        return super.delete("").then((response => response.json()));
    }
    save() {
        return this.update(this.data);
    }
}

class Job extends Endpoint {
    constructor(data, httpClient) {
        super(`/jobs/${data.id}`, httpClient);
        this.data = data;
    }
    get assemblyId() {
        return this.data.assemblyId;
    }
    get authorId() {
        return this.data.authorId;
    }
    get createdAt() {
        return this.data.createdAt;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        this._data = value;
    }
    get done() {
        return this.data.status === "done" || this.data.status === "failed";
    }
    get fileId() {
        return this.data.fileId;
    }
    get id() {
        return this.data.id;
    }
    get lastUpdate() {
        return this.data.lastUpdate;
    }
    get outputFormat() {
        return this.data.outputFormat;
    }
    get parameters() {
        return this.data.parameters;
    }
    get status() {
        return this.data.status;
    }
    get statusMessage() {
        return this.data.statusMessage;
    }
    get startedAt() {
        return this.data.startedAt;
    }
    async checkout() {
        const response = await this.get("");
        this.data = await response.json();
        return this;
    }
    async update(data) {
        const response = await this.put("", data);
        this.data = await response.json();
        return this;
    }
    delete() {
        return super.delete("").then((response => response.json()));
    }
    waitForDone(params) {
        const checkDone = () => this.checkout().then((job => {
            var _a;
            const ready = [ "done", "failed" ].includes(job.status);
            const cancel = (_a = params === null || params === undefined ? undefined : params.onCheckout) === null || _a === undefined ? undefined : _a.call(params, job, ready);
            return cancel || ready;
        }));
        return waitFor(checkDone, params).then((() => this));
    }
}

class SharedLink extends Endpoint {
    constructor(data, httpClient) {
        super(`/shares/${data.token}`, httpClient);
        this.data = data;
    }
    get createdAt() {
        return this.data.createdAt;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        this._data = value;
    }
    get permissions() {
        return this.data.permissions;
    }
    set permissions(value) {
        this.data.permissions = {
            ...this.data.permissions,
            ...value
        };
    }
    get token() {
        return this.data.token;
    }
    get url() {
        return this.data.url;
    }
    async checkout() {
        const response = await this.get("");
        this.data = await response.json();
        return this;
    }
    async update(data) {
        const response = await this.put("", data);
        this.data = await response.json();
        return this;
    }
    delete() {
        return super.delete("").then((response => response.json()));
    }
    save() {
        return this.update(this.data);
    }
}

class File extends Endpoint {
    constructor(data, httpClient) {
        super(`/files/${data.id}`, httpClient);
        this.data = data;
    }
    get activeVersion() {
        return this.data.activeVersion;
    }
    get created() {
        return this.data.created;
    }
    get customFields() {
        return this.data.customFields;
    }
    set customFields(value) {
        this.data.customFields = value;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
        var _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
        this._data = value;
        this._data.previewUrl = value.preview ? `${this.httpClient.serverUrl}${this.path}/preview?updated=${value.updatedAt}` : "";
        if (typeof this._data.owner === "string") this._data.owner = {
            userId: this._data.owner
        };
        (_a = (_p = this._data).owner) !== null && _a !== undefined ? _a : _p.owner = {};
        this._data.owner.avatarUrl = `${this.httpClient.serverUrl}/users/${this._data.owner.userId}/avatar`;
        this._data.owner.fullName = userFullName(this._data.owner);
        this._data.owner.initials = userInitials(this._data.owner.fullName);
        (_b = (_q = this._data).status) !== null && _b !== undefined ? _b : _q.status = {};
        (_c = (_r = this._data.status).geometry) !== null && _c !== undefined ? _c : _r.geometry = {
            state: (_d = this._data.geometryStatus) !== null && _d !== undefined ? _d : "none"
        };
        (_e = (_s = this._data.status).properties) !== null && _e !== undefined ? _e : _s.properties = {
            state: (_f = this._data.propertiesStatus) !== null && _f !== undefined ? _f : "none"
        };
        (_g = (_t = this._data.status).validation) !== null && _g !== undefined ? _g : _t.validation = {
            state: (_h = this._data.validationStatus) !== null && _h !== undefined ? _h : "none"
        };
        (_j = (_u = this._data).updatedBy) !== null && _j !== undefined ? _j : _u.updatedBy = {};
        this._data.updatedBy.avatarUrl = `${this.httpClient.serverUrl}/users/${this._data.updatedBy.userId}/avatar`;
        this._data.updatedBy.fullName = userFullName(this._data.updatedBy);
        this._data.updatedBy.initials = userInitials(this._data.updatedBy.fullName);
        (_k = (_v = this._data).versions) !== null && _k !== undefined ? _k : _v.versions = [ {
            ...value
        } ];
        (_l = (_w = this._data.status).geometryGltf) !== null && _l !== undefined ? _l : _w.geometryGltf = {
            state: "none"
        };
        (_m = (_x = this._data).isFileDeleted) !== null && _m !== undefined ? _m : _x.isFileDeleted = false;
        (_o = (_y = this._data).sharedLinkToken) !== null && _o !== undefined ? _o : _y.sharedLinkToken = null;
    }
    get exports() {
        return this.data.exports;
    }
    get geometryType() {
        if (this.status.geometryGltf.state === "done") return "gltf"; else if (this.status.geometry.state === "done") return "vsfx"; else return "";
    }
    get id() {
        return this.data.id;
    }
    get isFileDeleted() {
        return this.data.isFileDeleted;
    }
    get name() {
        return this.data.name;
    }
    set name(value) {
        this.data.name = value;
    }
    get originalFileId() {
        return this.data.originalFileId;
    }
    get owner() {
        return this.data.owner;
    }
    get previewUrl() {
        return this.data.previewUrl;
    }
    get size() {
        return this.data.size;
    }
    get sizeTotal() {
        return this.data.sizeTotal;
    }
    get sharedLinkToken() {
        return this.data.sharedLinkToken;
    }
    get status() {
        return this.data.status;
    }
    get type() {
        return this.data.type;
    }
    get updatedAt() {
        return this.data.updatedAt;
    }
    get updatedBy() {
        return this.data.updatedBy;
    }
    get version() {
        return this.data.version;
    }
    get versions() {
        return this.data.versions;
    }
    async checkout() {
        const response = await this.get("");
        this.data = await response.json();
        return this;
    }
    async update(data) {
        const response = await this.put("", data);
        this.data = await response.json();
        return this;
    }
    delete() {
        return super.delete("").then((response => response.json()));
    }
    save() {
        return this.update(this.data);
    }
    async setPreview(image) {
        if (!image) {
            await this.deletePreview();
        } else {
            const response = await this.post("/preview", image);
            this.data = await response.json();
        }
        return this;
    }
    async deletePreview() {
        const response = await super.delete("/preview");
        this.data = await response.json();
        return this;
    }
    getModels() {
        return this.get("/geometry").then((response => response.json())).then((array => array.map((data => new Model(data, this)))));
    }
    getModelTransformMatrix(handle) {
        return undefined;
    }
    setModelTransformMatrix(handle, transform) {
        console.warn("File does not support model transformation");
        return Promise.resolve(this);
    }
    getProperties(handles, group = false) {
        const searchParams = new URLSearchParams;
        if (handles) {
            if (Array.isArray(handles)) handles = handles.join(",");
            if (typeof handles === "string") handles = handles.trim();
            if (handles) searchParams.set("handles", handles);
        }
        if (group) searchParams.set("group", "true");
        let queryString = searchParams.toString();
        if (queryString) queryString = "?" + queryString;
        return this.get(`/properties${queryString}`).then((response => response.json()));
    }
    searchProperties(searchPattern) {
        return this.post("/properties/search", searchPattern).then((response => response.json()));
    }
    getCdaTree() {
        return this.get(`/properties/tree`).then((response => response.json()));
    }
    getViewpoints() {
        return this.get("/viewpoints").then((response => response.json())).then((viewpoints => viewpoints.result));
    }
    saveViewpoint(viewpoint) {
        return this.post("/viewpoints", viewpoint).then((response => response.json()));
    }
    deleteViewpoint(guid) {
        return super.delete(`/viewpoints/${guid}`).then((response => response.json()));
    }
    getSnapshot(guid) {
        return this.get(`/viewpoints/${guid}/snapshot`).then((response => response.text()));
    }
    getSnapshotData(guid, bitmapGuid) {
        return this.get(`/viewpoints/${guid}/bitmaps/${bitmapGuid}`).then((response => response.text()));
    }
    download(onProgress, signal) {
        return this.httpClient.downloadFile(this.getEndpointPath("/downloads"), onProgress, {
            signal: signal,
            headers: this.headers
        }).then((response => response.arrayBuffer()));
    }
    downloadResource(dataId, onProgress, signal) {
        return this.httpClient.downloadFile(this.getEndpointPath(`/downloads/${dataId}`), onProgress, {
            signal: signal,
            headers: this.headers
        }).then((response => response.arrayBuffer()));
    }
    downloadResourceRange(dataId, requestId, ranges, onProgress, signal) {
        return this.httpClient.downloadFileRange(this.getEndpointPath(`/downloads/${dataId}${requestId ? "?requestId=" + requestId : ""}`), requestId, ranges, onProgress, {
            signal: signal,
            headers: this.headers
        }).then((response => response.arrayBuffer()));
    }
    partialDownloadResource(dataId, onProgress, signal) {
        console.warn("File.partialDownloadResource() has been deprecated since 25.3 and will be removed in a future release, use File.downloadResource() instead.");
        return this.downloadResource(dataId, onProgress, signal);
    }
    async downloadFileRange(requestId, records, dataId, onProgress, signal) {
        await this.downloadResourceRange(dataId, requestId, records, onProgress, signal);
    }
    getReferences(signal) {
        return this.get("/references", signal).then((response => response.json()));
    }
    setReferences(references) {
        return this.put("/references", references).then((response => response.json()));
    }
    createJob(outputFormat, parameters) {
        const jobs = new Endpoint("/jobs", this.httpClient, this.headers);
        return jobs.post(this.appendVersionParam(""), {
            fileId: this.id,
            outputFormat: outputFormat,
            parameters: parseArgs(parameters)
        }).then((response => response.json())).then((data => new Job(data, this.httpClient)));
    }
    extractGeometry(type, parameters) {
        return this.createJob(type === "gltf" ? "geometryGltf" : "geometry", parameters);
    }
    extractProperties(parameters) {
        return this.createJob("properties", parameters);
    }
    validate(parameters) {
        return this.createJob("validation", parameters);
    }
    waitForDone(jobs, waitAll, params) {
        const waitJobs = Array.isArray(jobs) ? jobs : [ jobs ];
        if (waitAll === undefined) waitAll = true;
        const checkDone = () => this.checkout().then((file => {
            var _a;
            const readyJobs = waitJobs.filter((job => {
                const jobStatus = file.status[job] || {};
                return [ "none", "done", "failed" ].includes(jobStatus.state || "none");
            }));
            const ready = waitAll ? readyJobs.length === waitJobs.length : readyJobs.length > 0;
            const cancel = (_a = params === null || params === undefined ? undefined : params.onCheckout) === null || _a === undefined ? undefined : _a.call(params, file, ready);
            return cancel || ready;
        }));
        return waitFor(checkDone, params).then((() => this));
    }
    getPermissions() {
        return this.get("/permissions").then((response => response.json())).then((array => array.map((data => new Permission(data, this.id, this.httpClient)))));
    }
    getPermission(permissionId) {
        return this.get(`/permissions/${permissionId}`).then((response => response.json())).then((data => new Permission(data, this.id, this.httpClient)));
    }
    createPermission(actions, grantedTo, _public) {
        return this.post("/permissions", {
            actions: Array.isArray(actions) ? actions : [ actions ],
            grantedTo: grantedTo,
            public: _public
        }).then((response => response.json())).then((data => new Permission(data, this.id, this.httpClient)));
    }
    deletePermission(permissionId) {
        return super.delete(`/permissions/${permissionId}`).then((response => response.json()));
    }
    async uploadVersion(file, params = {
        waitForDone: false
    }) {
        const result = await this.httpClient.uploadFile(this.getEndpointPath("/versions"), file, (progress => {
            var _a;
            return (_a = params.onProgress) === null || _a === undefined ? undefined : _a.call(params, progress, file);
        }), {
            headers: this.headers
        }).then((xhr => JSON.parse(xhr.responseText))).then((data => new File(data, this.httpClient)));
        let geometryType = "";
        if (this.versions[0].status.geometryGltf.state !== "none") geometryType = "gltf";
        if (this.versions[0].status.geometry.state !== "none") geometryType = "vsfx";
        params = {
            ...params
        };
        if (params.geometry === undefined) params.geometry = geometryType !== "";
        if (params.properties === undefined) params.properties = this.versions[0].status.properties.state !== "none";
        const jobs = [];
        if (params.geometry) jobs.push((await result.extractGeometry(geometryType)).outputFormat);
        if (params.properties) jobs.push((await result.extractProperties()).outputFormat);
        if (jobs.length > 0) if (params.waitForDone) await result.waitForDone(jobs, true, params); else await result.checkout();
        await this.checkout();
        return result;
    }
    getVersions() {
        return this.get("/versions").then((response => response.json())).then((files => files.map((data => new File(data, this.httpClient))))).then((files => files.map((file => file.id == file.originalFileId ? file.useVersion(0) : file))));
    }
    getVersion(version) {
        return this.get(`/versions/${version}`).then((response => response.json())).then((data => new File(data, this.httpClient))).then((file => file.id == file.originalFileId ? file.useVersion(0) : file));
    }
    async deleteVersion(version) {
        const response = await super.delete(`/versions/${version}`);
        const data = await response.json();
        await this.checkout();
        return data;
    }
    setActiveVersion(version) {
        return this.update({
            activeVersion: version
        });
    }
    useVersion(version) {
        return super.useVersion(version);
    }
    async deleteSource() {
        const response = await super.delete("/source");
        this.data = await response.json();
        return this;
    }
    async createSharedLink(permissions) {
        const shares = new Endpoint("/shares", this.httpClient, this.headers);
        const response = await shares.post("", {
            fileId: this.id,
            permissions: permissions
        });
        const data = await response.json();
        await this.checkout();
        return new SharedLink(data, this.httpClient);
    }
    async getSharedLink() {
        if (!this.sharedLinkToken) return Promise.resolve(undefined);
        const shares = new Endpoint("/shares", this.httpClient, this.headers);
        const response = await shares.get(`/${this.sharedLinkToken}`);
        const data = await response.json();
        return new SharedLink(data, this.httpClient);
    }
    async deleteSharedLink() {
        const shares = new Endpoint("/shares", this.httpClient, this.headers);
        const response = await shares.delete(`/${this.sharedLinkToken}`);
        const data = await response.json();
        await this.checkout();
        return data;
    }
}

class Role extends Endpoint {
    constructor(data, projectId, httpClient) {
        super("", httpClient);
        this.projectId = projectId;
        this.data = data;
    }
    get description() {
        return this.data.description;
    }
    set description(value) {
        this._data.description = value;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        this._data = value;
        this.path = `/projects/${this.projectId}/roles/${value.name}`;
    }
    get name() {
        return this.data.name;
    }
    set name(value) {
        this._data.name = value;
    }
    get permissions() {
        return this.data.permissions;
    }
    set permissions(value) {
        this.data.permissions = value || {};
    }
    async checkout() {
        const response = await this.get("");
        this.data = await response.json();
        return this;
    }
    async update(data) {
        const response = await this.put("", data);
        this.data = await response.json();
        return this;
    }
    delete() {
        return super.delete("").then((response => response.json()));
    }
    save() {
        return this.update(this.data);
    }
}

class Member extends Endpoint {
    constructor(data, projectId, httpClient) {
        super(`/projects/${projectId}/members/${data.id}`, httpClient);
        this.data = data;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        this._data = value;
        this._data.user.avatarUrl = `${this.httpClient.serverUrl}/users/${this._data.user.userId}/avatar`;
        this._data.user.fullName = userFullName(this._data.user);
        this._data.user.initials = userInitials(this._data.user.fullName);
    }
    get id() {
        return this.data.id;
    }
    get role() {
        return this.data.role;
    }
    set role(value) {
        this.data.role = value;
    }
    get type() {
        return this.data.type;
    }
    get user() {
        return this.data.user;
    }
    async checkout() {
        const response = await this.get("");
        this.data = await response.json();
        return this;
    }
    async update(data) {
        const response = await this.put("", data);
        this.data = await response.json();
        return this;
    }
    delete() {
        return super.delete("").then((response => response.json()));
    }
    save() {
        return this.update(this.data);
    }
}

class Project extends Endpoint {
    constructor(data, httpClient) {
        super(`/projects/${data.id}`, httpClient);
        this.data = data;
    }
    get authorization() {
        return this.data.authorization;
    }
    get createdAt() {
        return this.data.createdAt;
    }
    get customFields() {
        return this.data.customFields;
    }
    set customFields(value) {
        this.data.customFields = value;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        this._data = value;
        this._data.previewUrl = value.avatarUrl ? `${this.httpClient.serverUrl}/projects/${this._data.id}/preview?updated=${value.updatedAt}` : "";
        this._data.owner.avatarUrl = `${this.httpClient.serverUrl}/users/${this._data.owner.userId}/avatar`;
        this._data.owner.fullName = userFullName(this._data.owner);
        this._data.owner.initials = userInitials(this._data.owner.fullName);
    }
    get description() {
        return this.data.description;
    }
    set description(value) {
        this.data.description = value;
    }
    get endDate() {
        return this.data.endDate;
    }
    set endDate(value) {
        this.data.endDate = value instanceof Date ? value.toISOString() : value;
    }
    get id() {
        return this.data.id;
    }
    get memberCount() {
        return this.data.memberCount;
    }
    get modelCount() {
        return this.data.modelCount;
    }
    get name() {
        return this.data.name;
    }
    set name(value) {
        this.data.name = value;
    }
    get owner() {
        return this.data.owner;
    }
    get previewUrl() {
        return this._data.previewUrl;
    }
    get public() {
        return this.data.public;
    }
    set public(value) {
        this.data.public = value;
    }
    get startDate() {
        return this.data.startDate;
    }
    set startDate(value) {
        this.data.startDate = value instanceof Date ? value.toISOString() : value;
    }
    get topicCount() {
        return this.data.topicCount;
    }
    get updatedAt() {
        return this.data.updatedAt;
    }
    async checkout() {
        const response = await this.get("");
        this.data = await response.json();
        return this;
    }
    async update(data) {
        const response = await this.put("", data);
        this.data = await response.json();
        return this;
    }
    delete() {
        return super.delete("").then((response => response.text())).then((text => {
            try {
                return JSON.parse(text);
            } catch {
                return {
                    id: this.id
                };
            }
        }));
    }
    save() {
        return this.update(this.data);
    }
    async setPreview(image) {
        if (!image) {
            await this.deletePreview();
        } else {
            const response = await this.post("/preview", image);
            this.data = await response.json();
        }
        return this;
    }
    async deletePreview() {
        const response = await super.delete("/preview");
        this.data = await response.json();
        return this;
    }
    getRoles() {
        return this.get("/roles").then((response => response.json())).then((array => array.map((data => new Role(data, this.id, this.httpClient)))));
    }
    getRole(name) {
        return this.get(`/roles/${name}`).then((response => response.json())).then((data => new Role(data, this.id, this.httpClient)));
    }
    createRole(name, description, permissions) {
        return this.post("/roles", {
            name: name,
            description: description,
            permissions: permissions || {}
        }).then((response => response.json())).then((data => new Role(data, this.id, this.httpClient)));
    }
    deleteRole(name) {
        return super.delete(`/roles/${name}`).then((response => response.json()));
    }
    getMembers() {
        return this.get("/members").then((response => response.json())).then((array => array.map((data => new Member(data, this.id, this.httpClient)))));
    }
    getMember(memberId) {
        return this.get(`/members/${memberId}`).then((response => response.json())).then((data => new Member(data, this.id, this.httpClient)));
    }
    addMember(userId, role) {
        return this.post("/members", {
            userId: userId,
            role: role
        }).then((response => response.json())).then((data => new Member(data, this.id, this.httpClient)));
    }
    removeMember(memberId) {
        return super.delete(`/members/${memberId}`).then((response => response.json()));
    }
    getFilesInformation() {
        const bcfProjects = new Endpoint("/bcf/3.0/projects", this.httpClient, this.headers);
        return bcfProjects.get(`/${this.id}/files_information`).then((response => response.json())).then((items => {
            items.forEach((item => {
                const getFieldValue = displayName => (item.display_information.find((x => x.field_display_name === displayName)) || {}).field_value;
                const previewUrl = `${this.httpClient.serverUrl}/files/${item.file.reference}/preview`;
                const ownerAvatarUrl = `${this.httpClient.serverUrl}/users/${getFieldValue("Owner")}/avatar`;
                const ownerFirstName = getFieldValue("Owner First Name");
                const ownerLastName = getFieldValue("Owner Last Name");
                const ownerUserName = getFieldValue("Owner User Name");
                const ownerFullName = userFullName(ownerFirstName, ownerLastName, ownerUserName);
                const ownerInitials = userInitials(ownerFullName);
                item.display_information.push({
                    field_display_name: "Preview URL",
                    field_value: previewUrl
                });
                item.display_information.push({
                    field_display_name: "Owner Avatar URL",
                    field_value: ownerAvatarUrl
                });
                item.display_information.push({
                    field_display_name: "Owner Full Name",
                    field_value: ownerFullName
                });
                item.display_information.push({
                    field_display_name: "Owner Initials",
                    field_value: ownerInitials
                });
                const updatedByAvatarUrl = `${this.httpClient.serverUrl}/users/${getFieldValue("Updated By")}/avatar`;
                const updatedByFirstName = getFieldValue("Updated By First Name");
                const updatedByLastName = getFieldValue("Updated By Last Name");
                const updatedByUserName = getFieldValue("Updated By User Name");
                const updatedByFullName = userFullName(updatedByFirstName, updatedByLastName, updatedByUserName);
                const updatedByInitials = userInitials(updatedByFullName);
                item.display_information.push({
                    field_display_name: "Updated By Avatar URL",
                    field_value: updatedByAvatarUrl
                });
                item.display_information.push({
                    field_display_name: "Updated By Full Name",
                    field_value: updatedByFullName
                });
                item.display_information.push({
                    field_display_name: "Updated By Initials",
                    field_value: updatedByInitials
                });
                const geometry = getFieldValue("Geometry Status");
                const geometryGltf = getFieldValue("GeometryGltf Status");
                const geometryType = geometry === "done" ? "vsfx" : geometryGltf === "done" ? "gltf" : "";
                item.display_information.push({
                    field_display_name: "Geometry Type",
                    field_value: geometryType
                });
            }));
            return items;
        }));
    }
    getModels() {
        return this.getFilesInformation().then((filesInformation => filesInformation.map((item => item.file.reference)))).then((ids => {
            const files = new Endpoint("/files", this.httpClient, this.headers);
            return files.get(`?id=${ids.join("|")}`);
        })).then((response => response.json())).then((files => files.result.map((data => new File(data, this.httpClient)))));
    }
    async addModel(fileId, actions, _public) {
        const files = new Endpoint("/files", this.httpClient, this.headers);
        const file = await files.get(`/${fileId}`).then((response => response.json())).then((data => new File(data, this.httpClient)));
        const grantedTo = [ {
            project: {
                id: this.id,
                name: this.name
            }
        } ];
        await file.createPermission(actions, grantedTo, _public);
        return file;
    }
    async removeModel(fileId) {
        const files = new Endpoint("/files", this.httpClient, this.headers);
        const file = await files.get(`/${fileId}`).then((response => response.json())).then((data => new File(data, this.httpClient)));
        const permissions = await file.getPermissions();
        await Promise.allSettled(permissions.filter((permission => permission.grantedTo.some((x => {
            var _a;
            return ((_a = x.project) === null || _a === undefined ? undefined : _a.id) === this.id;
        })))).map((permission => permission.delete())));
        return file;
    }
}

class User extends Endpoint {
    constructor(data, httpClient) {
        super("", httpClient);
        this.data = data;
    }
    get avatarUrl() {
        return this._data.avatarUrl;
    }
    get canCreateProject() {
        return this.data.canCreateProject;
    }
    set canCreateProject(value) {
        this._data.canCreateProject = value;
    }
    get createAt() {
        return this.data.createAt;
    }
    get customFields() {
        return this.data.customFields;
    }
    set customFields(value) {
        this._data.customFields = value;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        this._data = value;
        this._data.avatarUrl = value.avatarImage ? `${this.httpClient.serverUrl}/users/${this._data.id}/avatar?updated=${value.lastModified}` : "";
        this._data.fullName = userFullName(this._data);
        this._data.initials = userInitials(this._data.fullName);
    }
    get email() {
        return this.data.email;
    }
    set email(value) {
        this._data.email = value;
    }
    get emailConfirmationId() {
        return this.data.emailConfirmationId;
    }
    get firstName() {
        return this.data.firstName;
    }
    set firstName(value) {
        this._data.firstName = value;
    }
    get fullName() {
        return this.data.fullName;
    }
    get id() {
        return this.data.id;
    }
    get initials() {
        return this.data.initials;
    }
    get isAdmin() {
        return this.data.isAdmin;
    }
    set isAdmin(value) {
        this._data.isAdmin = value;
    }
    get isEmailConfirmed() {
        return this.data.isEmailConfirmed;
    }
    get lastModified() {
        return this.data.lastModified;
    }
    get lastName() {
        return this.data.lastName;
    }
    set lastName(value) {
        this._data.lastName = value;
    }
    get lastSignIn() {
        return this.data.lastSignIn;
    }
    get projectsLimit() {
        return this.data.projectsLimit;
    }
    set projectsLimit(value) {
        this._data.projectsLimit = value;
    }
    get providerType() {
        return this.data.providerType;
    }
    get storageLimit() {
        return this.data.storageLimit;
    }
    set storageLimit(value) {
        this._data.storageLimit = value;
    }
    get storageUsed() {
        return this.data.storageUsed;
    }
    get token() {
        return this.data.tokenInfo.token;
    }
    get userName() {
        return this.data.userName;
    }
    set userName(value) {
        this._data.userName = value;
    }
    async checkout() {
        if (this.httpClient.signInUserIsAdmin) {
            const response = await this.get(`/users/${this.id}`);
            const data = await response.json();
            this.data = {
                id: data.id,
                ...data.userBrief
            };
        } else if (this.id === this.httpClient.signInUserId) {
            const response = await this.get("/user");
            const data = await response.json();
            this.data = {
                id: this.id,
                ...data
            };
        } else {
            return Promise.reject(new FetchError(403));
        }
        return this;
    }
    async update(data) {
        if (this.httpClient.signInUserIsAdmin) {
            const response = await this.put(`/users/${this.id}`, {
                isAdmin: data.isAdmin,
                userBrief: data
            });
            const newData = await response.json();
            this.data = {
                id: newData.id,
                ...newData.userBrief
            };
        } else if (this.id === this.httpClient.signInUserId) {
            const response = await this.put("/user", data);
            const newData = await response.json();
            this.data = {
                id: this.id,
                ...newData
            };
        } else {
            return Promise.reject(new FetchError(403));
        }
        return this;
    }
    delete() {
        if (this.httpClient.signInUserIsAdmin) {
            return super.delete(`/users/${this.id}`).then((response => response.json())).then((data => {
                if (this.id === this.httpClient.signInUserId) {
                    delete this.httpClient.headers["Authorization"];
                    this.httpClient.signInUserId = "";
                    this.httpClient.signInUserIsAdmin = false;
                }
                return data;
            }));
        } else {
            return Promise.reject(new FetchError(403));
        }
    }
    save() {
        return this.update(this.data);
    }
    async setAvatar(image) {
        if (!image) {
            await this.deleteAvatar();
        } else if (this.httpClient.signInUserIsAdmin) {
            const response = await this.post(`/users/${this.id}/avatar`, image);
            const data = await response.json();
            this.data = {
                id: data.id,
                ...data.userBrief
            };
        } else if (this.id === this.httpClient.signInUserId) {
            const response = await this.post("/user/avatar", image);
            const data = await response.json();
            this.data = {
                id: this.id,
                ...data
            };
        } else {
            return Promise.reject(new FetchError(403));
        }
        return this;
    }
    async deleteAvatar() {
        if (this.httpClient.signInUserIsAdmin) {
            const response = await super.delete(`/users/${this.id}/avatar`);
            const data = await response.json();
            this.data = {
                id: data.id,
                ...data.userBrief
            };
        } else if (this.id === this.httpClient.signInUserId) {
            const response = await super.delete("/user/avatar");
            const data = await response.json();
            this.data = {
                id: this.id,
                ...data
            };
        } else {
            return Promise.reject(new FetchError(403));
        }
        return this;
    }
    async changePassword(newPassword, oldPassword) {
        if (this.httpClient.signInUserIsAdmin) {
            const response = await this.put(`/users/${this.id}/password`, {
                new: newPassword
            });
            const data = await response.json();
            this.data = {
                id: data.id,
                ...data.userBrief
            };
        } else if (this.id === this.httpClient.signInUserId) {
            const response = await this.put("/user/password", {
                old: oldPassword,
                new: newPassword
            });
            const data = await response.json();
            this.data = {
                id: this.id,
                ...data
            };
        } else {
            return Promise.reject(new FetchError(403));
        }
        return this;
    }
}

class OAuthClient extends Endpoint {
    constructor(data, httpClient) {
        super(`/oauth/clients/${data.clientId}`, httpClient);
        this.data = data;
    }
    get authUrl() {
        return this.data.authUrl;
    }
    get accessTokenUrl() {
        return this.data.accessTokenUrl;
    }
    get clientId() {
        return this.data.clientId;
    }
    get createdAt() {
        return this.data.createdAt;
    }
    get description() {
        return this.data.description;
    }
    set description(value) {
        this._data.description = value;
    }
    get data() {
        return this._data;
    }
    set data(value) {
        this._data = value;
    }
    get name() {
        return this.data.name;
    }
    set name(value) {
        this._data.name = value;
    }
    get redirectUrl() {
        return this.data.redirectUrl;
    }
    set redirectUrl(value) {
        this.data.redirectUrl = value;
    }
    get secret() {
        return this.data.secret;
    }
    get updatedAt() {
        return this.data.updatedAt;
    }
    async checkout() {
        const response = await this.get("");
        this.data = await response.json();
        return this;
    }
    async update(data) {
        const response = await this.put("", data);
        this.data = await response.json();
        return this;
    }
    delete() {
        return super.delete("").then((response => response.json()));
    }
    save() {
        return this.update(this.data);
    }
    async revoke() {
        await this.post("/revoke");
        return this;
    }
}

class SharedFile extends File {
    constructor(data, password, httpClient) {
        super(data.file, httpClient);
        this.path = `/shares/${data.file.sharedLinkToken}`;
        this.headers = {
            "InWeb-Password": password
        };
    }
    async checkout() {
        const response = await this.get("/info");
        const data = await response.json();
        this.data = data.file;
        return this;
    }
    async update(data) {
        const response = await this.put("/info", data);
        this.data = await response.json();
        return this;
    }
    getVersions() {
        return Promise.resolve(undefined);
    }
    useVersion(version) {
        return this;
    }
}

class Client extends EventEmitter2 {
    constructor(params = {}) {
        super();
        this._serverUrl = "";
        this._httpClient = new HttpClient("");
        this._user = null;
        this.eventEmitter = this;
        this.configure(params);
    }
    get serverUrl() {
        return this._serverUrl;
    }
    get httpClient() {
        return this._httpClient;
    }
    get options() {
        console.warn("Client.options has been deprecated since 25.3 and will be removed in a future release, use Viewer.options instead.");
        const data = {
            showWCS: true,
            cameraAnimation: true,
            antialiasing: true,
            groundShadow: false,
            shadows: false,
            cameraAxisXSpeed: 4,
            cameraAxisYSpeed: 1,
            ambientOcclusion: false,
            enableStreamingMode: true,
            enablePartialMode: false,
            memoryLimit: 3294967296,
            cuttingPlaneFillColor: {
                red: 255,
                green: 152,
                blue: 0
            },
            edgesColor: {
                r: 255,
                g: 152,
                b: 0
            },
            facesColor: {
                r: 255,
                g: 152,
                b: 0
            },
            edgesVisibility: true,
            edgesOverlap: true,
            facesOverlap: false,
            facesTransparancy: 200,
            enableCustomHighlight: true,
            sceneGraph: false,
            edgeModel: true,
            reverseZoomWheel: false,
            enableZoomWheel: true,
            enableGestures: true
        };
        return {
            ...data,
            data: data,
            defaults: () => data,
            resetToDefaults: () => {},
            saveToStorage: () => {},
            loadFromStorage: () => {}
        };
    }
    configure(params) {
        this._serverUrl = (params.serverUrl || "").replace(/\/+$/, "");
        this._httpClient = new HttpClient(this.serverUrl);
        this._user = null;
        return this;
    }
    version() {
        return this.httpClient.get("/version").then((response => response.json())).then((data => ({
            ...data,
            server: data.version,
            client: "26.9.1"
        })));
    }
    registerUser(email, password, userName) {
        return this.httpClient.post("/register", {
            email: email,
            password: password,
            userName: userName !== null && userName !== undefined ? userName : (email + "").split("@").at(0)
        }).then((response => response.json()));
    }
    resendConfirmationEmail(email, password) {
        return this.httpClient.post("/register/email-confirmation", {
            email: email,
            password: password
        }).then((response => response.json()));
    }
    confirmUserEmail(emailConfirmationId) {
        return this.httpClient.get(`/register/email-confirmation/${emailConfirmationId}`).then((response => response.json()));
    }
    async signInWithEmail(email, password) {
        const credentials = btoa(unescape(encodeURIComponent(email + ":" + password)));
        this.httpClient.headers["Authorization"] = "Basic " + credentials;
        const response = await this.httpClient.get("/token");
        const data = await response.json();
        return this.setCurrentUser(data);
    }
    async signInWithToken(token) {
        this.httpClient.headers["Authorization"] = token;
        const response = await this.httpClient.get("/user");
        const data = await response.json();
        return this.setCurrentUser(data);
    }
    signOut() {
        this.clearCurrentUser();
    }
    setCurrentUser(data) {
        this._user = new User(data, this.httpClient);
        this.httpClient.headers["Authorization"] = data.tokenInfo.token;
        this.httpClient.signInUserId = this._user.id;
        this.httpClient.signInUserIsAdmin = this._user.isAdmin;
        return this._user;
    }
    clearCurrentUser() {
        this._user = null;
        delete this.httpClient.headers["Authorization"];
        this.httpClient.signInUserId = "";
        this.httpClient.signInUserIsAdmin = false;
    }
    getCurrentUser() {
        if (this._user && !this.httpClient.signInUserId) this._user = null;
        return this._user;
    }
    getIdentityProviders() {
        return this.httpClient.get("/identity").then((response => response.json()));
    }
    getServerSettings() {
        return this.httpClient.get("/settings").then((response => response.json()));
    }
    updateServerSettings(settings) {
        return this.httpClient.put("/settings", settings).then((response => response.json()));
    }
    getOAuthClients(start, limit) {
        const searchParams = new URLSearchParams;
        if (start > 0) searchParams.set("start", start.toString());
        if (limit > 0) searchParams.set("limit", limit.toString());
        let queryString = searchParams.toString();
        if (queryString) queryString = "?" + queryString;
        return this.httpClient.get(`/oauth/clients${queryString}`).then((response => response.json())).then((clients => ({
            ...clients,
            result: clients.result.map((data => new OAuthClient(data, this.httpClient)))
        })));
    }
    getOAuthClient(clientId) {
        return this.httpClient.get(`/oauth/clients/${clientId}`).then((response => response.json())).then((data => new OAuthClient(data, this.httpClient)));
    }
    createOAuthClient(name, redirectUrl, description) {
        return this.httpClient.post("/oauth/clients", {
            name: name,
            redirectUrl: redirectUrl,
            description: description
        }).then((response => response.json())).then((data => new OAuthClient(data, this.httpClient)));
    }
    deleteOAuthClient(clientId) {
        return this.httpClient.delete(`/oauth/clients/${clientId}`).then((response => response.json()));
    }
    getUsers() {
        return this.httpClient.get("/users").then((response => response.json())).then((array => array.map((data => ({
            id: data.id,
            ...data.userBrief
        }))))).then((array => array.map((data => new User(data, this.httpClient)))));
    }
    getUser(userId) {
        if (this.httpClient.signInUserIsAdmin) {
            return this.httpClient.get(`/users/${userId}`).then((response => response.json())).then((data => ({
                id: data.id,
                ...data.userBrief
            }))).then((data => new User(data, this.httpClient)));
        } else if (userId === this.httpClient.signInUserId) {
            return this.httpClient.get("/user").then((response => response.json())).then((data => ({
                id: userId,
                ...data
            }))).then((data => new User(data, this.httpClient)));
        } else {
            return Promise.reject(new FetchError(403));
        }
    }
    createUser(email, password, params = {}) {
        const {isAdmin: isAdmin, userName: userName, ...rest} = params;
        return this.httpClient.post("/users", {
            isAdmin: isAdmin,
            userBrief: {
                ...rest,
                email: email,
                userName: userName !== null && userName !== undefined ? userName : (email + "").split("@").at(0)
            },
            password: password
        }).then((response => response.json())).then((data => ({
            id: data.id,
            ...data.userBrief
        }))).then((data => new User(data, this.httpClient)));
    }
    deleteUser(userId) {
        if (this.httpClient.signInUserIsAdmin) {
            return this.httpClient.delete(`/users/${userId}`).then((response => response.json())).then((data => {
                if (userId === this.httpClient.signInUserId) {
                    this.clearCurrentUser();
                }
                return data;
            }));
        } else {
            return Promise.reject(new FetchError(403));
        }
    }
    getFiles(start, limit, name, ext, ids, sortByDesc, sortField, shared) {
        const searchParams = new URLSearchParams;
        if (start > 0) searchParams.set("start", start.toString());
        if (limit > 0) searchParams.set("limit", limit.toString());
        if (name) searchParams.set("name", name);
        if (ext) {
            if (Array.isArray(ext)) ext = ext.join("|");
            if (typeof ext === "string") ext = ext.toLowerCase();
            if (ext) searchParams.set("ext", ext);
        }
        if (ids) {
            if (Array.isArray(ids)) ids = ids.join("|");
            searchParams.set("id", ids);
        }
        if (sortByDesc !== undefined) searchParams.set("sortBy", sortByDesc ? "desc" : "asc");
        if (sortField) searchParams.set("sortField", sortField);
        if (shared) searchParams.set("shared", "true");
        let queryString = searchParams.toString();
        if (queryString) queryString = "?" + queryString;
        return this.httpClient.get(`/files${queryString}`).then((response => response.json())).then((files => ({
            ...files,
            result: files.result.map((data => new File(data, this.httpClient)))
        })));
    }
    getFile(fileId) {
        return this.httpClient.get(`/files/${fileId}`).then((response => response.json())).then((data => new File(data, this.httpClient)));
    }
    async uploadFile(file, params = {
        geometry: true,
        properties: false,
        waitForDone: false
    }) {
        const result = await this.httpClient.uploadFile("/files", file, (progress => {
            var _a;
            this.emitEvent({
                type: "uploadprogress",
                data: progress,
                file: file
            });
            (_a = params.onProgress) === null || _a === undefined ? undefined : _a.call(params, progress, file);
        })).then((xhr => JSON.parse(xhr.responseText))).then((data => new File(data, this.httpClient)));
        const geometryType = typeof params.geometry === "string" ? params.geometry : "vsfx";
        const jobParameters = params.jobParameters || {};
        const jobs = [];
        if (params.geometry) jobs.push((await result.extractGeometry(geometryType, jobParameters.geometry)).outputFormat);
        if (params.properties) jobs.push((await result.extractProperties(jobParameters.properties)).outputFormat);
        if (jobs.length > 0) if (params.waitForDone) await result.waitForDone(jobs, true, params); else await result.checkout();
        return result;
    }
    deleteFile(fileId) {
        return this.httpClient.delete(`/files/${fileId}`).then((response => response.json()));
    }
    downloadFile(fileId, onProgress, signal) {
        return this.httpClient.downloadFile(`/files/${fileId}/downloads`, onProgress, {
            signal: signal
        }).then((response => response.arrayBuffer()));
    }
    getJobs(status, limit, start, sortByDesc, sortField) {
        const searchParams = new URLSearchParams;
        if (start > 0) searchParams.set("start", start.toString());
        if (limit > 0) searchParams.set("limit", limit.toString());
        if (status) {
            if (Array.isArray(status)) status = status.join("|");
            if (typeof status === "string") status = status.trim().toLowerCase();
            if (status) searchParams.set("status", status);
        }
        if (sortByDesc !== undefined) searchParams.set("sortBy", sortByDesc ? "desc" : "asc");
        if (sortField) searchParams.set("sortField", sortField);
        let queryString = searchParams.toString();
        if (queryString) queryString = "?" + queryString;
        return this.httpClient.get(`/jobs${queryString}`).then((response => response.json())).then((jobs => ({
            ...jobs,
            result: jobs.result.map((data => new Job(data, this.httpClient)))
        })));
    }
    getJob(jobId) {
        return this.httpClient.get(`/jobs/${jobId}`).then((response => response.json())).then((data => new Job(data, this.httpClient)));
    }
    createJob(fileId, outputFormat, parameters) {
        return this.httpClient.post("/jobs", {
            fileId: fileId,
            outputFormat: outputFormat,
            parameters: parseArgs(parameters)
        }).then((response => response.json())).then((data => new Job(data, this.httpClient)));
    }
    deleteJob(jobId) {
        return this.httpClient.delete(`/jobs/${jobId}`).then((response => response.json()));
    }
    getAssemblies(start, limit, name, ids, sortByDesc, sortField) {
        const searchParams = new URLSearchParams;
        if (start > 0) searchParams.set("start", start.toString());
        if (limit > 0) searchParams.set("limit", limit.toString());
        if (name) searchParams.set("name", name);
        if (ids) {
            if (Array.isArray(ids)) ids = ids.join("|");
            if (typeof ids === "string") ids = ids.trim();
            if (ids) searchParams.set("id", ids);
        }
        if (sortByDesc !== undefined) searchParams.set("sortBy", sortByDesc ? "desc" : "asc");
        if (sortField) searchParams.set("sortField", sortField);
        let queryString = searchParams.toString();
        if (queryString) queryString = "?" + queryString;
        return this.httpClient.get(`/assemblies${queryString}`).then((response => response.json())).then((assemblies => ({
            ...assemblies,
            result: assemblies.result.map((data => new Assembly(data, this.httpClient)))
        })));
    }
    getAssembly(assemblyId) {
        return this.httpClient.get(`/assemblies/${assemblyId}`).then((response => response.json())).then((data => new Assembly(data, this.httpClient)));
    }
    createAssembly(files, name, params = {}) {
        const jobParameters = params.jobParameters || {};
        return this.httpClient.post("/assemblies", {
            name: name,
            files: files,
            jobParameters: {
                geometry: parseArgs(jobParameters.geometry),
                properties: parseArgs(jobParameters.properties)
            }
        }).then((response => response.json())).then((data => new Assembly(data, this.httpClient))).then((result => params.waitForDone ? result.waitForDone(params) : result));
    }
    deleteAssembly(assemblyId) {
        return this.httpClient.delete(`/assemblies/${assemblyId}`).then((response => response.json()));
    }
    getProjects(start, limit, name, ids, sortByDesc) {
        const searchParams = new URLSearchParams;
        if (start > 0) searchParams.set("start", start.toString());
        if (limit > 0) searchParams.set("limit", limit.toString());
        if (name) searchParams.set("name", name);
        if (ids) {
            if (Array.isArray(ids)) ids = ids.join("|");
            if (typeof ids === "string") ids = ids.trim();
            if (ids) searchParams.set("id", ids);
        }
        if (sortByDesc !== undefined) searchParams.set("sortBy", sortByDesc ? "desc" : "asc");
        let queryString = searchParams.toString();
        if (queryString) queryString = "?" + queryString;
        return this.httpClient.get(`/projects${queryString}`).then((response => response.json())).then((projects => {
            if (Array.isArray(projects)) {
                let result = projects;
                if (ids) result = result.filter((x => ids.includes(x.id)));
                if (name) result = result.filter((x => x.name.includes(name)));
                if (limit > 0) {
                    const begin = start > 0 ? start : 0;
                    result = result.slice(begin, begin + limit);
                }
                return {
                    allSize: projects.length,
                    start: start,
                    limit: limit,
                    result: result,
                    size: result.length
                };
            }
            return projects;
        })).then((projects => ({
            ...projects,
            result: projects.result.map((data => new Project(data, this.httpClient)))
        })));
    }
    getProject(projectId) {
        return this.httpClient.get(`/projects/${projectId}`).then((response => response.json())).then((data => new Project(data, this.httpClient)));
    }
    createProject(name, description, startDate, endDate) {
        return this.httpClient.post("/projects", {
            name: name,
            description: description,
            startDate: startDate instanceof Date ? startDate.toISOString() : startDate,
            endDate: endDate instanceof Date ? endDate.toISOString() : endDate
        }).then((response => response.json())).then((data => new Project(data, this.httpClient)));
    }
    deleteProject(projectId) {
        return this.httpClient.delete(`/projects/${projectId}`).then((response => response.text())).then((text => {
            try {
                return JSON.parse(text);
            } catch {
                return {
                    id: projectId
                };
            }
        }));
    }
    getSharedLink(token) {
        return this.httpClient.get(`/shares/${token}`).then((response => response.json())).then((data => new SharedLink(data, this.httpClient)));
    }
    createSharedLink(fileId, permissions) {
        return this.httpClient.post("/shares", {
            fileId: fileId,
            permissions: permissions
        }).then((response => response.json())).then((data => new SharedLink(data, this.httpClient)));
    }
    deleteSharedLink(token) {
        return this.httpClient.delete(`/shares/${token}`).then((response => response.json()));
    }
    getSharedFile(token, password) {
        return this.httpClient.get(`/shares/${token}/info`, {
            headers: {
                "InWeb-Password": password
            }
        }).then((response => response.json())).then((data => new SharedFile(data, password, this.httpClient)));
    }
}

const version = "26.9.1";

export { Assembly, ClashTest, Client, Endpoint, FetchError, File, Job, Member, Model, OAuthClient, Permission, Project, Role, SharedFile, SharedLink, User, parseArgs, statusText, userFullName, userInitials, version, waitFor };
//# sourceMappingURL=client.module.js.map