UNPKG

@nextgis/ngw-connector

Version:

A lightweight HTTP client optimized for use with NextGIS Web API

1,563 lines (1,540 loc) 54.4 kB
/** Bundle of @nextgis/ngw-connector; version: 3.0.1; author: NextGIS */ import { isObject as isObject$1, objectRemoveEmpty, fixUrlStr, objectDeepEqual, defined } from '@nextgis/utils'; import Cache from '@nextgis/cache'; import { EventEmitter } from 'events'; const templateRe = /\{ *([\w_-]+) *\}/g; function template(str, data) { return str.replace(templateRe, (s, key) => { let value = data[key]; if (value === void 0) { throw new Error("No value provided for letiable " + s); } else if (typeof value === "function") { value = value(data); } return value; }); } var __defProp$b = Object.defineProperty; var __getOwnPropSymbols$4 = Object.getOwnPropertySymbols; var __hasOwnProp$4 = Object.prototype.hasOwnProperty; var __propIsEnum$4 = Object.prototype.propertyIsEnumerable; var __defNormalProp$b = (obj, key, value) => key in obj ? __defProp$b(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$4 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$4.call(b, prop)) __defNormalProp$b(a, prop, b[prop]); if (__getOwnPropSymbols$4) for (var prop of __getOwnPropSymbols$4(b)) { if (__propIsEnum$4.call(b, prop)) __defNormalProp$b(a, prop, b[prop]); } return a; }; var __async$4 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; function apiRequest(opt) { return __async$4(this, null, function* () { const { params, name, connector, requestOptions } = opt; const apiItems = yield connector.connect(); let apiItem = apiItems && apiItems[name]; if (apiItem) { apiItem = [...apiItem]; let url = apiItem.shift(); if (apiItem.length) { const replaceParams = {}; for (let fry = 0; fry < apiItem.length; fry++) { const arg = apiItem[fry]; replaceParams[fry] = `{${arg}}`; if (params[arg] === void 0) { throw new Error(`\`${arg}\` URL API argument is not specified`); } } if (url) { url = template(url, replaceParams); } } if (params) { const paramArray = []; const paramList = params.paramList; if (Array.isArray(paramList)) { paramList.forEach(([key, value]) => { paramArray.push(`${key}=${value}`); }); } for (const p in params) { if (apiItem.indexOf(p) === -1) { paramArray.push(`${p}=${params[p]}`); } } if (paramArray.length) { url = `${url}?${paramArray.join("&")}`; } } if (url) { return connector.makeQuery(url, params, __spreadValues$4({ cacheName: name }, requestOptions)); } else { throw new Error("Request URL is not set"); } } else { return void 0; } }); } var pkg = { name: "@nextgis/ngw-connector", version: "3.0.1", _priority: 12, description: "A lightweight HTTP client optimized for use with NextGIS Web API", main: "index.js", module: "lib/ngw-connector.esm-bundler.js", unpkg: "lib/ngw-connector.global.prod.js", jsdelivr: "lib/ngw-connector.global.prod.js", types: "lib/index.d.ts", dependencies: { "@nextgis/cache": "3.0.0", "@nextgis/cancelable-promise": "3.0.0", "@nextgis/utils": "3.0.0", "@types/events": "^3.0.0", events: "*", "form-data": "^4.0.0" }, devDependencies: { "@nextgis/build-tools": "3.0.0" }, scripts: { clean: "rimraf ./lib", dev: "node ../build-tools/lib/build.js", prod: "npm run dev -- --release", lint: "eslint ./src/**/*.ts --fix --c ../../.eslintrc", watch: "npm run dev -- --watch", "gen:types": "node ./scripts/generator" }, buildOptions: { name: "NgwConnector", formats: [ "esm-bundler", "esm-browser", "cjs", "global" ] }, keywords: [ "NextGIS", "MAP" ], author: "NextGIS", files: [ "index.js", "lib" ], license: "MIT", homepage: "https://github.com/nextgis/nextgis_frontend/tree/master/packages/ngw-connector#readme", repository: { type: "git", url: "git://github.com/nextgis/nextgis_frontend.git" }, engines: { node: ">=18.20.4" }, gitHead: "edc68033f914f6c95c4125b9738bba6d36e990b4" }; class AbortError extends Error { constructor(message = "AbortError") { super(message); this.name = "AbortError"; } } var __defProp$a = Object.defineProperty; var __defNormalProp$a = (obj, key, value) => key in obj ? __defProp$a(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$8 = (obj, key, value) => __defNormalProp$a(obj, typeof key !== "symbol" ? key + "" : key, value); class NgwError extends Error { constructor(er) { super(); __publicField$8(this, "name", "NgwError"); __publicField$8(this, "title"); __publicField$8(this, "message"); __publicField$8(this, "detail"); __publicField$8(this, "exception"); __publicField$8(this, "status_code"); __publicField$8(this, "data"); __publicField$8(this, "guru_meditation"); Object.assign(this, er); Object.setPrototypeOf(this, NgwError.prototype); } } var __defProp$9 = Object.defineProperty; var __defNormalProp$9 = (obj, key, value) => key in obj ? __defProp$9(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$7 = (obj, key, value) => __defNormalProp$9(obj, typeof key !== "symbol" ? key + "" : key, value); class InsufficientPermissionsError extends NgwError { constructor(obj) { super(obj); __publicField$7(this, "name", "InsufficientPermissionsError"); __publicField$7(this, "exception", "nextgisweb.core.exception.InsufficientPermissions"); Object.setPrototypeOf(this, InsufficientPermissionsError.prototype); } } var __defProp$8 = Object.defineProperty; var __defNormalProp$8 = (obj, key, value) => key in obj ? __defProp$8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$6 = (obj, key, value) => __defNormalProp$8(obj, typeof key !== "symbol" ? key + "" : key, value); let NetworksResponseError$1 = class NetworksResponseError extends NgwError { constructor(obj) { super(obj); __publicField$6(this, "message", "There is no response from the server or problem connecting to server."); __publicField$6(this, "title", "Network error"); __publicField$6(this, "detail", "Check network connectivity and try again later."); Object.setPrototypeOf(this, NetworksResponseError.prototype); } }; function extractError(error) { if (isObject$1(error)) { if (error.name && error.message && error.title) { return { title: error.title, message: error.message, detail: error.detail || null, data: error.data && error.data.data ? error.data.data : null }; } else if (error.exception) { if (error.status === void 0 || error.status === 0 || error.data === void 0) { return new NetworksResponseError$1({ title: error.title, status_code: error.status_code, exception: error.exception }); } } return { title: typeof error.title === "string" ? error.title : "Unexpected error", message: typeof error.message === "string" ? error.message : "Something went wrong." }; } } function isError(error) { if (isObject$1(error)) { return error.status_code && error.exception && error.title; } return false; } var __defProp$7 = Object.defineProperty; var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$5 = (obj, key, value) => __defNormalProp$7(obj, key + "" , value); class NetworkError extends Error { constructor(url) { super(); __publicField$5(this, "name", "NetworkError"); Object.setPrototypeOf(this, NetworkError.prototype); this.message = `Unable to request ${url}. Possibly invalid NGW URL entered or CORS not configured to get request from ${location.origin}`; } } var __defProp$6 = Object.defineProperty; var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$4 = (obj, key, value) => __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value); class ResourceNotFoundError extends NgwError { constructor(obj) { super(obj); __publicField$4(this, "name", "ResourceNotFoundError"); __publicField$4(this, "exception", "nextgisweb.resource.exception.ResourceNotFound"); Object.setPrototypeOf(this, ResourceNotFoundError.prototype); } } var errors = /*#__PURE__*/Object.freeze({ __proto__: null, AbortError: AbortError, InsufficientPermissionsError: InsufficientPermissionsError, NetworkError: NetworkError, NetworksResponseError: NetworksResponseError$1, NgwError: NgwError, ResourceNotFoundError: ResourceNotFoundError, extractError: extractError, isError: isError }); var __defProp$5 = Object.defineProperty; var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$3 = (obj, key, value) => __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value); class BaseAPIError extends Error { constructor(message) { super(message || "Something went wrong."); __publicField$3(this, "title"); this.name = "BaseAPIError"; this.title = "Unknown API error"; if (Error.captureStackTrace) { Error.captureStackTrace(this, BaseAPIError); } } } class NetworksResponseError extends BaseAPIError { // prettier-ignore constructor(message) { super(message || "There is no response from the server or problem connecting to server."); __publicField$3(this, "detail"); this.title = "Network error"; this.detail = "Check network connectivity and try again later."; } } class InvalidResponseError extends BaseAPIError { constructor(message) { super(message || "Something went wrong."); this.title = "Unexpected server response"; } } class ServerResponseError extends BaseAPIError { constructor(data) { super(data.message); __publicField$3(this, "detail"); __publicField$3(this, "data"); this.title = data.title || this.title; this.detail = data.detail || null; this.data = data; } } class LunkwillError extends Error { // prettier-ignore constructor(message, data = {}) { super(message || "Unexpected error while processing long-running request."); __publicField$3(this, "title"); __publicField$3(this, "data"); this.name = "LunkwillError"; this.title = "Long-running request error"; this.data = data; if (Error.captureStackTrace) { Error.captureStackTrace(this, LunkwillError); } } } class LunkwillRequestCancelled extends LunkwillError { constructor(data) { super("Long-running request was cancelled.", data); } } class LunkwillRequestFailed extends LunkwillError { constructor(data) { super(void 0, data); } } var __defProp$4 = Object.defineProperty; var __getOwnPropSymbols$3 = Object.getOwnPropertySymbols; var __hasOwnProp$3 = Object.prototype.hasOwnProperty; var __propIsEnum$3 = Object.prototype.propertyIsEnumerable; var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$3 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$3.call(b, prop)) __defNormalProp$4(a, prop, b[prop]); if (__getOwnPropSymbols$3) for (var prop of __getOwnPropSymbols$3(b)) { if (__propIsEnum$3.call(b, prop)) __defNormalProp$4(a, prop, b[prop]); } return a; }; var __objRest$2 = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp$3.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols$3) for (var prop of __getOwnPropSymbols$3(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum$3.call(source, prop)) target[prop] = source[prop]; } return target; }; var __async$3 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; function lunkwillCheckResponse(lwResp) { const ct = lwResp.headers.get("content-type"); return ct !== void 0 && ct !== null && ct.includes("application/vnd.lunkwill.request-summary+json"); } function responseJson(response) { return __async$3(this, null, function* () { try { return response.json(); } catch (e) { throw new Error(); } }); } function lunkwillResponseUrl(lwResp) { return __async$3(this, null, function* () { const lwData = yield responseJson(lwResp); let delay = lwData.delay_ms; const retry = lwData.retry_ms !== void 0 ? lwData.retry_ms : 2e3; const sum = `/api/lunkwill/${lwData.id}/summary`; const res = `/api/lunkwill/${lwData.id}/response`; const sleep = (msec) => new Promise((resolve) => setTimeout(resolve, msec)); let failed = false; let ready = false; while (!ready) { yield sleep(failed ? retry : delay); failed = false; let lwResp2; let lwData2; try { lwResp2 = yield fetch(sum, { credentials: "same-origin" }); lwData2 = yield lwResp2.json(); } catch (e) { failed = true; continue; } switch (lwData2.status) { case void 0: throw new LunkwillError(void 0, lwData2); case "ready": ready = true; break; case "cancelled": throw new LunkwillRequestCancelled(lwData2); case "failed": throw new LunkwillRequestFailed(lwData2); case "spooled": case "processing": case "buffering": delay = lwData2.delay_ms; break; default: throw new LunkwillError(void 0, lwData2); } } return res; }); } function lunkwillFetch(lwRespUrl) { return __async$3(this, null, function* () { try { return yield window.fetch(lwRespUrl, { credentials: "same-origin" }); } catch (e) { throw new NetworksResponseError(); } }); } const mediaContentTypes = [ "application/pdf", "image/png", "image/jpeg", "image/tiff", "text/csv" ]; const mediaContentTypesRegex = new RegExp(mediaContentTypes.join("|")); function isMediaContentType(contentType) { return mediaContentTypesRegex.test(contentType); } function encodeQueryParams(value) { const result = []; for (const [k, v] of Object.entries(value)) { if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") { result.push(`${k}=${encodeURIComponent(v)}`); } else if (Array.isArray(v)) { result.push(`${k}=${v.map(encodeURIComponent).join(",")}`); } else { for (const [sk, sv] of Object.entries(v)) { const ske = `${k}[${encodeURIComponent(sk)}]`; if (typeof sv === "string" || typeof sv === "number" || typeof sv === "boolean") { result.push(`${ske}=${encodeURIComponent(sv)}`); } else if (Array.isArray(sv)) { const sve = sv.map(encodeURIComponent); result.push(`${ske}=${sve.join(",")}`); } } } } return result.join("&"); } function generateUrl(path, query) { let urlParams = ""; if (query !== void 0) { urlParams = "?" + encodeQueryParams(query); } return path + urlParams; } function request(path, options, cacheEngine) { return __async$3(this, null, function* () { var _b, _c; const defaults = { method: "GET", credentials: "same-origin", headers: {} }; const _a = __spreadValues$3(__spreadValues$3({}, defaults), options), { withCredentials, responseType, cacheProps, cacheName, lunkwill, cache, query, json } = _a, opt = __objRest$2(_a, [ "withCredentials", "responseType", "cacheProps", "cacheName", "lunkwill", "cache", "query", "json" ]); opt.method = (_b = opt.method) == null ? void 0 : _b.toUpperCase(); if (withCredentials) { opt.credentials = "include"; } let useLunkwill = false; if (lunkwill !== void 0) { lunkwill.toHeaders(opt.headers || {}); useLunkwill = true; } const lunkwillReturnUrl = !!opt.lunkwillReturnUrl; delete opt.lunkwillReturnUrl; if (json !== void 0) { opt.body = JSON.stringify(json); const headers = opt.headers || {}; headers["Content-Type"] = "application/json"; opt.headers = headers; } const url = generateUrl(path, query); const makeRequest = () => __async$3(null, null, function* () { let response; try { response = yield fetch(url, opt); } catch (e) { if (opt.signal && opt.signal.aborted) { throw e; } throw new NetworksResponseError(); } if (useLunkwill && lunkwillCheckResponse(response)) { const lwRespUrl = yield lunkwillResponseUrl(response); if (lunkwillReturnUrl) { return lwRespUrl; } response = yield lunkwillFetch(lwRespUrl); } const respCType = response.headers.get("content-type"); const respJSON = respCType && (respCType.includes("application/json") || respCType.includes("application/vnd.lunkwill.request-summary+json")); let body; try { const respMedia = respCType && isMediaContentType(respCType); if (responseType === "blob" || respMedia) { body = yield response.blob(); } else if (respJSON) { body = yield response.json(); } else { throw new InvalidResponseError(); } } catch (e) { if (e.name === "AbortError" || e instanceof InvalidResponseError) { throw e; } throw new InvalidResponseError(); } if (400 <= response.status && response.status <= 599) { throw new ServerResponseError(body); } return body; }); if (cacheEngine) { if (((_c = opt.method) == null ? void 0 : _c.toUpperCase()) === "GET") { if (cache !== false) { const props = cacheProps ? cacheProps : __spreadValues$3({}, objectRemoveEmpty({ withCredentials, responseType })); return cacheEngine.add(cacheName || url, makeRequest, { props, expirationTime: cache ? void 0 : 500 }); } } else { const ignoredForClean = ["api/feature_layer/identify"]; if (ignoredForClean.every((part) => !path.includes(part))) { cacheEngine.clean(); } } } return makeRequest(); }); } var __defProp$3 = Object.defineProperty; var __defProps$2 = Object.defineProperties; var __getOwnPropDescs$2 = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols$2 = Object.getOwnPropertySymbols; var __hasOwnProp$2 = Object.prototype.hasOwnProperty; var __propIsEnum$2 = Object.prototype.propertyIsEnumerable; var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$2 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$2.call(b, prop)) __defNormalProp$3(a, prop, b[prop]); if (__getOwnPropSymbols$2) for (var prop of __getOwnPropSymbols$2(b)) { if (__propIsEnum$2.call(b, prop)) __defNormalProp$3(a, prop, b[prop]); } return a; }; var __spreadProps$2 = (a, b) => __defProps$2(a, __getOwnPropDescs$2(b)); var __objRest$1 = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp$2.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols$2) for (var prop of __getOwnPropSymbols$2(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum$2.call(source, prop)) target[prop] = source[prop]; } return target; }; var __async$2 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; function routeURL(name, baseUrl, routeData, ...rest) { const [template, ...params] = routeData[name]; const first = rest[0]; let sub; if (first === void 0) { sub = []; } else if (typeof first === "object" && first !== null) { if (rest.length > 1) { throw new Error("Too many arguments for route(name, object)!"); } sub = []; for (const [p, v] of Object.entries(first)) { sub[params.indexOf(p)] = String(v); } } else { sub = rest.map((v) => String(v)); } return fixUrlStr( baseUrl + template.replace(/\{(\w+)\}/g, function(m, a) { const idx = parseInt(a); const value = sub[idx]; if (value === void 0) { const msg = `Undefined parameter ${idx} in "${template}".`; throw new Error(msg); } return String(value); }) ); } function route(name, connector, ...rest) { const result = { url: (opt) => __async$2(null, null, function* () { var _a; const routeData = yield connector.connect(); const template = routeURL( name, (_a = connector.options.baseUrl) != null ? _a : "", routeData, ...rest ); return generateUrl(template, opt == null ? void 0 : opt.query); }) }; const methods = ["get", "post", "put", "delete", "patch"]; for (const method of methods) { const methodResp = (requestOptions) => { var _b; const _a = requestOptions || {}, { headers: optHeaders } = _a, restOpt = __objRest$1(_a, ["headers"]); if ((_b = requestOptions == null ? void 0 : requestOptions.signal) == null ? void 0 : _b.aborted) { throw new AbortError(); } return connector.connect().then((routeData) => { var _a2; const template = routeURL( name, (_a2 = connector.options.baseUrl) != null ? _a2 : "", routeData, ...rest ); const headers = objectRemoveEmpty(__spreadValues$2(__spreadValues$2({}, connector.getAuthorizationHeaders()), optHeaders != null ? optHeaders : {})); return request( template, __spreadProps$2(__spreadValues$2({ headers }, restOpt), { method }), connector.cache ); }); }; result[method] = methodResp; } return result; } function isObject(val) { return Object.prototype.toString.call(val) === "[object Object]"; } let loadData; { loadData = (url, callback, options = {}, error, onCancel) => { options.method = options.method || "GET"; const xhr = new XMLHttpRequest(); xhr.open(options.method || "GET", url, true); if (options.responseType === "blob") { xhr.responseType = options.responseType; } const getResponseText = () => { try { return JSON.parse(xhr.responseText); } catch (e) { return xhr.responseText; } }; const processingResponse = (forError = false) => { const cb = forError ? error : callback; if (options.responseType === "blob") { cb(xhr.response); } else { if (xhr.responseText) { cb(getResponseText()); } else { error({ message: "" }); } } }; xhr.onload = () => { if ([401, 403, 404, 422, 500].indexOf(xhr.status) !== -1) { error(new NgwError(getResponseText())); } processingResponse(); }; xhr.onerror = (er) => { if (xhr.status === 0) { error(new NetworkError(url)); } else { error(er); } }; xhr.upload.onprogress = function(e) { if (e.lengthComputable) { const percentComplete = e.loaded / e.total * 100; if (options.onProgress) { options.onProgress(percentComplete, e); } } }; const headers = options.headers; if (headers) { for (const h in headers) { const header = headers[h]; if (typeof header === "string") { xhr.setRequestHeader(h, header); } } } if (options.withCredentials !== void 0) { xhr.withCredentials = options.withCredentials; } let data; if (options.file) { data = new FormData(); data.append("file", options.file); if (options.data) { for (const d in options.data) { data.append(d, data[d]); } } } else { data = options.data ? typeof options.data === "string" ? options.data : JSON.stringify(options.data) : null; } if (onCancel) { onCancel(() => { xhr.abort(); }); } xhr.send(data); }; } const CONNECTORS = []; function addConnector(connector) { CONNECTORS.push(connector); } function findConnector(options) { return CONNECTORS.find((x) => { if (x.options.baseUrl === options.baseUrl) { if (options.auth) { if (x.options.auth) { return objectDeepEqual(x.options.auth, options.auth); } } else { return true; } } }); } function removeConnector(connector) { const index = CONNECTORS.indexOf(connector); if (index !== -1) { CONNECTORS.splice(index, 1); } } var __defProp$2 = Object.defineProperty; var __defProps$1 = Object.defineProperties; var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols; var __hasOwnProp$1 = Object.prototype.hasOwnProperty; var __propIsEnum$1 = Object.prototype.propertyIsEnumerable; var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$1 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$1.call(b, prop)) __defNormalProp$2(a, prop, b[prop]); if (__getOwnPropSymbols$1) for (var prop of __getOwnPropSymbols$1(b)) { if (__propIsEnum$1.call(b, prop)) __defNormalProp$2(a, prop, b[prop]); } return a; }; var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp$1.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols$1) for (var prop of __getOwnPropSymbols$1(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum$1.call(source, prop)) target[prop] = source[prop]; } return target; }; var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value); var __async$1 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; let ID = 0; let REQUEST_ID = 0; class NgwConnector { constructor(options) { this.options = options; __publicField$2(this, "id", ID++); __publicField$2(this, "emitter", new EventEmitter()); __publicField$2(this, "user"); __publicField$2(this, "cache"); __publicField$2(this, "withCredentials"); // Dedicated cache for route requests, as these are executed before any other requests. // This cache does not need to be cleared during the session. __publicField$2(this, "routeCache"); __publicField$2(this, "client", `NextGIS-NGW-Connector/${pkg.version}`); __publicField$2(this, "routeStr", "/api/component/pyramid/route"); __publicField$2(this, "activeRequests", {}); __publicField$2(this, "requestTransform"); const exist = findConnector(options); this.cache = new Cache({ namespace: options.cacheId }); this.routeCache = new Cache({ namespace: "routecache" }); if (exist) { return exist; } else { const { route: route2, requestTransform, withCredentials } = this.options; if (route2) { this.routeStr = route2; } if (requestTransform) { this.requestTransform = requestTransform; } if (withCredentials !== void 0) { this.withCredentials = withCredentials; } addConnector(this); } } /** * Clear the cache. */ clearCache() { this.cache.clean(); } setRequestTransform(requestTransform) { this.requestTransform = requestTransform; } /** * Fast way to specify the connection address to NextGIS Web. * The current connection will be severed. * @param baseUrl - NGW url */ setNgw(baseUrl) { this.logout(); this.options.baseUrl = baseUrl; addConnector(this); } /** * Establishing a connection with NextGIS Web to fulfill all other requests. * @remarks * This method need not be called manually as it is used when forming a request in {@link apiRequest | apiRequest}. * Can be used to check connection. * @example * ```javascript * const connector = new NgwConnector({ baseUrl: 'https://demo.nextgis.com' }); * connector.connect() * .then(() => console.log('Ok')) * .catch((er) => console.log('Connection problem', er)); * ``` */ connect() { return __async$1(this, arguments, function* ({ signal } = {}) { const auth = this.options.auth; if (auth) { const { login, password } = auth; if (login && password) { yield this._login({ login, password }); } } const routeUrl = `${this.routeStr}?client=${this.client}`; return this.routeCache.add( this.options.baseUrl || String(this.id), () => this.makeQuery(routeUrl, null, { signal, cache: false }) ); }); } /** * Quick way to change NextGIS Web user. * @param credentials - New user credentials */ login(credentials, options) { this.logout(); addConnector(this); return this._login(credentials, options); } /** * Disconnecting a user. Aborting all current requests */ logout() { this.abort(); removeConnector(this); this.options.auth = void 0; this.user = void 0; this.routeCache.clean(); this.clearCache(); this.emitter.emit("logout"); } getUserInfo(credentials, options) { return __async$1(this, null, function* () { if (this.user && this.user.id) { return this.user; } if (credentials) { this.options.auth = credentials; } const options_ = __spreadValues$1({ headers: this.getAuthorizationHeaders(credentials), cache: true }, options); return this.makeQuery( "/api/component/auth/current_user", {}, options_ ); }); } /** * Obtaining the required Headers for authentication of requests in the NGW. */ getAuthorizationHeaders(credentials) { const client = this.makeClientId(credentials); if (client) { return { Authorization: `Basic ${client}` }; } return {}; } makeClientId(credentials) { credentials = credentials || this.options.auth; if (credentials) { const { login, password } = credentials; const encodedStr = `${login}:${password}`; { return window.btoa(encodedStr); } } } /** Stop all api requests */ abort() { for (const abortController of Object.values(this.activeRequests)) { abortController.abort(); } this.activeRequests = {}; } getActiveApiRequests() { return __spreadValues$1({}, this.activeRequests); } route(name, ...rest) { return route(name, this, ...rest); } /** * Send request to NGW. * @param url - URL address to NGW * @param params - Query params * @param options - Request options */ makeQuery(_0, _1) { return __async$1(this, arguments, function* (url, params, options = {}) { var _b; url = (this.options.baseUrl ? this.options.baseUrl : "") + url; if (!url) { throw new Error("Empty `url` not allowed"); } if (params) { const _a = params, restParams = __objRest(_a, ["paramList"]); url = template(url, restParams); } url = encodeURI(fixUrlStr(url)); options = __spreadValues$1({ withCredentials: this.withCredentials }, options); const { cache, signal: externalSignal, method = "GET", headers, cacheName, cacheProps, responseType, withCredentials } = options; const internalAbortController = new AbortController(); const internalSignal = internalAbortController.signal; if (externalSignal) { if (externalSignal.aborted) { throw new AbortError(); } externalSignal.addEventListener("abort", () => { internalAbortController.abort(); }); } options.signal = internalSignal; const createPromise = () => __async$1(this, null, function* () { const id = REQUEST_ID++; this.activeRequests[id] = internalAbortController; try { return this._loadData(url, options); } finally { this._cleanActiveRequest(id); } }); if (method === "GET" && cache !== false) { const cacheOptions = cacheProps ? cacheProps : __spreadProps$1(__spreadValues$1({}, objectRemoveEmpty({ headers, withCredentials, responseType, baseUrl: this.options.baseUrl, userId: (_b = this.user) == null ? void 0 : _b.id })), { params }); return this.cache.add(cacheName || url, createPromise, { props: cacheOptions, expirationTime: cache ? void 0 : 500 }); } return createPromise(); }); } _loadData(url, options) { options.responseType = options.responseType || "json"; return new Promise((resolve, reject) => { var _a; if (this.user) { options = options || {}; options.headers = __spreadValues$1(__spreadValues$1({}, this.getAuthorizationHeaders()), options.headers); } if (this.requestTransform) { const [transUrl, transOptions] = this.requestTransform(url, options); url = transUrl; options = transOptions; } let runOnAbort = void 0; loadData(url, resolve, options, reject, (handler) => { runOnAbort = handler; }); (_a = options.signal) == null ? void 0 : _a.addEventListener("abort", () => { if (runOnAbort !== void 0) { runOnAbort(); } reject(new AbortError()); }); }).catch((httpError) => { if (httpError.name !== "AbortError") { if (!!(process.env.NODE_ENV !== "production")) { console.warn("DEV WARN", httpError); } const er = this._handleHttpError(httpError); if (er) { throw er; } } throw httpError; }); } _login(credentials, options) { return __async$1(this, null, function* () { try { const data = yield this.getUserInfo(credentials, options); this.user = data; this.emitter.emit("login", data); return data; } catch (er) { this.emitter.emit("login:error", er); throw er; } }); } _cleanActiveRequest(requestId) { delete this.activeRequests[requestId]; } _handleHttpError(er) { if (er) { if (er instanceof NgwError) { if (er.exception === "nextgisweb.resource.exception.ResourceNotFound") { throw new ResourceNotFoundError(er); } else if (er.exception === "nextgisweb.core.exception.InsufficientPermissions") { throw new InsufficientPermissionsError(er); } } } return er; } } __publicField$2(NgwConnector, "errors", errors); function resourceCompare(res1, res2) { return objectDeepEqual(res1, res2); } const exclude = ["description"]; function resourceToQuery(resource, prefix = "") { prefix = prefix ? prefix + "__" : ""; const query = {}; for (const [key, value] of Object.entries(resource)) { if (exclude.indexOf(key) === -1) { if (isObject(value)) { if (key === "owner_user") { const children = resourceToQuery( value, key ); Object.assign(query, children); } else if (key === "parent" && "id" in value) { query.parent_id = value.id; } } else if (defined(value)) { query[prefix + key] = value; } } } return query; } var __defProp$1 = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; class ResourcesControl { constructor({ connector, cacheId }) { __publicField$1(this, "cache"); __publicField$1(this, "connector"); this.connector = connector; this.cache = new Cache({ namespace: cacheId }); } // ------------------------------------------------------------------------- // Resource Methods // ------------------------------------------------------------------------- /** * Receive resource from NGW by id, keyname or search-object parameter. * @param resource - Resource id, keyname or search-object * * @remarks * Fetching resource would be cached to speed up next call */ getOne(resource, requestOptions) { const opt = __spreadValues({}, requestOptions); if (typeof resource === "string") ; else if (typeof resource === "number") ; else if (isObject(resource)) { if (resource.id !== void 0) { resource.id; } else { if (resource.keyname) { resource.keyname; } if (resource.display_name) { resource.display_name; } } } if (typeof resource === "string") { return this._fetchResourceBy({ keyname: resource }, opt); } else if (typeof resource === "number") { return this._fetchResourceById(resource, opt); } else if (isObject(resource)) { return this._fetchResourceBy(resource, opt); } return Promise.resolve(void 0); } getOneOrFail(resource, requestOptions) { return this.getOne(resource, requestOptions).then((res) => { if (res) { return res; } throw new ResourceNotFoundError(); }); } /** * A fast way to retrieve resource ID for any resource definition. * @param resource - Any available resource definition * * @remarks * There are situations when exactly the resource id is needed * (for example, to compose the correct request to the api) * then this method will come in handy to facilitate the extraction of the identifier * if the resource is specified through a keyname or other parameters. */ getId(resource, requestOptions) { if (typeof resource === "number") { return Promise.resolve(resource); } else if (typeof resource === "string" || isObject(resource)) { return this.getOne(resource, requestOptions).then((res) => { if (res) { return res.resource.id; } }); } return Promise.resolve(void 0); } /** * A fast way to retrieve resource ID for any resource definition. * @param resource - Any available resource definition * * @remarks * Similar with {@link NgwConnector.getResourceId | getResourceId} but rise error if resource is not exist. * To not make one more checks if the resource is definitely exists */ getIdOrFail(resource, requestOptions) { return this.getId(resource, requestOptions).then((resp) => { if (resp === void 0) { throw new Error(); } return resp; }); } getMany(resource, requestOptions) { return this._resourceCacheFilter(resource).then((items) => { if (!items.length) { const query = {}; if (resource.keyname) { query.keyname = resource.keyname; } else { Object.assign(query, resourceToQuery(resource)); } return this.connector.route("resource.search").get(__spreadProps(__spreadValues({}, requestOptions), { query: __spreadValues({ serialization: "full" }, query) })).then((resources) => { if ((requestOptions == null ? void 0 : requestOptions.cache) && resources) { for (const x of resources) { this.cache.add("resource.item", Promise.resolve(x), { id: x.resource.id }); } } return resources; }); } return items; }); } getParent(resource, requestOptions) { return this.getOne(resource, requestOptions).then((child) => { var _a, _b; if ((_b = (_a = child == null ? void 0 : child.resource) == null ? void 0 : _a.parent) == null ? void 0 : _b.id) { return this.getOne(child.resource.parent.id, requestOptions); } return Promise.resolve(void 0); }); } getChildrenOf(resource, requestOptions) { return this.getIdOrFail(resource).then( (parent) => this._getChildrenOf(parent, requestOptions) ); } update(resource, data) { return this.getId(resource).then((id) => { if (id !== void 0) { return this.connector.put("resource.item", { data }, { id }); } }); } /** * Fast way to delete resource from NGW and clean cache. * @param resource - Resource definition */ delete(resource) { return this.getId(resource).then((id) => { if (id !== void 0) { return this.connector.delete("resource.item", null, { id }).then(() => { this._cleanResourceItemCache(id); return void 0; }); } }); } _getChildrenOf(_0, _1) { return __async(this, arguments, function* (parentDef, requestOptions, _items = []) { let parent = void 0; if (typeof parentDef === "string") { parent = yield this.getId(parentDef, requestOptions); } else if (typeof parentDef === "object") { parent = parentDef.id; } else { parent = parentDef; } const items = yield this.connector.route("resource.collection").get(__spreadProps(__spreadValues({}, requestOptions), { query: { parent } })); const recursivePromises = []; for (const item of items) { if (requestOptions == null ? void 0 : requestOptions.cache) { this.cache.add("resource.item", Promise.resolve(item), { id: item.resource.id }); } _items.push(item); if ((requestOptions == null ? void 0 : requestOptions.recursive) && item.resource.children) { recursivePromises.push( this._getChildrenOf(item.resource.id, requestOptions, _items) ); } } if (recursivePromises.length) { return Promise.all(recursivePromises).then(() => { return _items; }); } return _items; }); } _cleanResourceItemCache(id) { return __async(this, null, function* () { var _a; const all = this.cache.all(); const toDelete = []; for (const c of all) { const cid = (_a = c.props) == null ? void 0 : _a.id; if (["resource.item", "resource"].includes(c.key) && cid !== void 0) { if (typeof cid === "number") { if (cid === id) { toDelete.push(c); } } else { const rid = yield this.getId(cid); if (rid === id) { toDelete.push(c); } } } } for (const d of toDelete) { this.cache.delete(d); } }); } _fetchResourceById(id, requestOptions) { return this.connector.route("resource.item", { id }).get(requestOptions); } _fetchResourceBy(resource, requestOptions) { return this.getMany(resource, requestOptions).then((resources) => { return resources[0]; }); } _resourceCacheFilter(resource) { return Promise.all(this.cache.matchAll("resource.item")).then( (resources) => { const items = []; resources.filter((x) => { if (x) { if (resource.keyname && x.resource.keyname) { return resource.keyname === x.resource.keyname; } if (defined(resource.id) && defined(x.resource.id)) { return resource.id === x.resource.id; } return resourceCompare(resource, x.resource); } }); return items; } ); } } var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value); class NgwConnectorExtended extends NgwConnector { constructor(options) { super(options); __publicField(this, "resources"); this.resources = new ResourcesControl({ connector: this, cacheId: options.cacheId }); } static create(options) { return new this(options); } /** * Clear the cache. */ clearCache() { super.clearCache(); this.resources.cache.clean(); } /** * Send request to NGW api router. * @param name - NGW route name from {@link https://docs.nextgis.com/docs_ngweb_dev/doc/developer/resource.html#routes | routes} * @param params - Request item params or query params * @param requestOptions - Request options * * @example * ```javascript * * // there is such an NGW route item * // "feature_layer.feature.item": [ * // "/api/resource/{0}/feature/{1}", * // "id", * // "fid" * // ], * * const connector = new NgwConnector({ baseUrl: 'https://example.nextgis.com' }); * connector.apiRequest('feature_layer.feature.item', { * // request params for {0} and {1} * 'id': 2011, * 'fid': 101, * // query params * 'srs': 4326, * 'geom_format': 'geojson', * }, { method: 'GET' }); * // send get-request to 'https://example.nextgis.com/api/resource/2011/feature/101?srs=4326&geom_format=geojson' * * ``` */ apiRequest(name, params_ = {}, requestOptions = {}) { var _a; params_ = (_a = requestOptions.params) != null ? _a : params_; const params = objectRemoveEmpty(params_); return apiRequest({ name, params, requestOptions, connector: this }); } /** * Shortcut method for send POST request to NGW. * @param name - NGW route name from {@link https://docs.nextgis.com/docs_ngweb_dev/doc/developer/reso