@nextgis/ngw-connector
Version:
A lightweight HTTP client optimized for use with NextGIS Web API
1,563 lines (1,540 loc) • 54.4 kB
JavaScript
/** 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