@servicestack/client
Version:
ServiceStack's TypeScript library providing convenience utilities in developing web apps. Integrates with ServiceStack's Server features including ServiceClient, Server Events, Error Handling and Validation
1,181 lines (1,180 loc) • 108 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sanitize = exports.camelCaseAny = exports.map = exports.toKebabCase = exports.toCamelCase = exports.toPascalCase = exports.createError = exports.isFormData = exports.createFieldError = exports.createErrorStatus = exports.ApiResult = exports.getResponseStatus = exports.getMethod = exports.JsonApiClient = exports.JsonServiceClient = exports.GetAccessTokenResponse = exports.HttpMethods = exports.ServerEventUser = exports.GetEventSubscribers = exports.UpdateEventSubscriberResponse = exports.UpdateEventSubscriber = exports.ServerEventReceiver = exports.getAllMembers = exports.ServerEventsClient = exports.ReadyState = exports.SingletonInstanceResolver = exports.NewInstanceResolver = exports.FieldCss = exports.KeyValuePair = exports.RefInfo = exports.FormatInfo = exports.InputInfo = exports.ImageInfo = exports.MetadataType = exports.MetadataPropertyType = exports.MetadataAttribute = exports.MetadataDataMember = exports.MetadataDataContract = exports.MetadataTypeName = exports.MetadataTypes = exports.MetadataOperationType = exports.MetadataRoute = exports.MetadataTypesConfig = exports.GetNavItemsResponse = exports.GetNavItems = exports.NavItem = exports.EmptyResponse = exports.ErrorResponse = exports.ResponseError = exports.ResponseStatus = void 0;
exports.$1 = exports.isElement = exports.createElement = exports.padStart = exports.msToTime = exports.toTime = exports.toLocalISOString = exports.timeFmt12 = exports.dateFmtHM = exports.dateFmt = exports.padInt = exports.toDateFmt = exports.toDate = exports.isDate = exports.errorResponse = exports.errorResponseExcept = exports.errorResponseSummary = exports.toObject = exports.toFormData = exports.parseResponseStatus = exports.getField = exports.normalize = exports.normalizeKey = exports.parseCookie = exports.tryDecode = exports.stripQuotes = exports.bytesToBase64 = exports.setQueryString = exports.appendQueryString = exports.createUrl = exports.createPath = exports.combinePaths = exports.queryString = exports.humanify = exports.splitTitleCase = exports.isDigit = exports.isLower = exports.isUpper = exports.ucFirst = exports.humanize = exports.onlyProps = exports.chop = exports.lastRightPart = exports.lastLeftPart = exports.rightPart = exports.leftPart = exports.splitOnLast = exports.splitOnFirst = exports.css = exports.nameOf = void 0;
exports.htmlAttrs = exports.enc = exports.uniq = exports.flatMap = exports.toTimeSpanFmt = exports.toXsdDuration = exports.fromXsdDuration = exports.classNames = exports.NavOptions = exports.UserAttributes = exports.LinkButtonDefaults = exports.NavButtonGroupDefaults = exports.NavbarDefaults = exports.NavLinkDefaults = exports.NavDefaults = exports.btnClasses = exports.btnSizeClass = exports.BootstrapSizes = exports.btnColorClass = exports.BootstrapColors = exports.activeClass = exports.activeClassNav = exports.apiValueFmt = exports.apiValue = exports.mapGet = exports.resolve = exports.each = exports.apply = exports.omitEmpty = exports.omit = exports.pick = exports.safeVarName = exports.trimEnd = exports.populateForm = exports.triggerEvent = exports.sanitizeFormData = exports.serializeToFormData = exports.serializeToUrlEncoded = exports.serializeToObject = exports.serializeForm = exports.ajaxSubmit = exports.formSubmit = exports.toVarNames = exports.bootstrapForm = exports.bindHandlers = exports.bootstrap = exports.delaySet = exports.addScript = exports.on = exports.$$ = void 0;
exports.Inspect = exports.createBus = exports.EventBus = exports.alignAuto = exports.alignRight = exports.alignCenter = exports.alignLeft = exports.uniqueKeys = exports.JSV = exports.StringBuffer = exports.toBase64String = exports.toByteArray = exports.fromByteArray = exports.toGuid = exports.fromGuid = exports.toTimeSpan = exports.fromTimeSpan = exports.toDateTime = exports.fromDateTime = exports.isNullOrEmpty = exports.indexOfAny = void 0;
class ResponseStatus {
constructor(init) { Object.assign(this, init); }
}
exports.ResponseStatus = ResponseStatus;
class ResponseError {
constructor(init) { Object.assign(this, init); }
}
exports.ResponseError = ResponseError;
class ErrorResponse {
constructor(init) { Object.assign(this, init); }
}
exports.ErrorResponse = ErrorResponse;
class EmptyResponse {
constructor(init) { Object.assign(this, init); }
}
exports.EmptyResponse = EmptyResponse;
class NavItem {
constructor(init) { Object.assign(this, init); }
}
exports.NavItem = NavItem;
class GetNavItems {
constructor(init) { Object.assign(this, init); }
createResponse() { return new GetNavItemsResponse(); }
getTypeName() { return 'GetNavItems'; }
getMethod() { return 'GET'; }
}
exports.GetNavItems = GetNavItems;
class GetNavItemsResponse {
constructor(init) { Object.assign(this, init); }
}
exports.GetNavItemsResponse = GetNavItemsResponse;
class MetadataTypesConfig {
constructor(init) { Object.assign(this, init); }
}
exports.MetadataTypesConfig = MetadataTypesConfig;
class MetadataRoute {
constructor(init) { Object.assign(this, init); }
}
exports.MetadataRoute = MetadataRoute;
class MetadataOperationType {
constructor(init) { Object.assign(this, init); }
}
exports.MetadataOperationType = MetadataOperationType;
class MetadataTypes {
constructor(init) { Object.assign(this, init); }
}
exports.MetadataTypes = MetadataTypes;
class MetadataTypeName {
constructor(init) { Object.assign(this, init); }
}
exports.MetadataTypeName = MetadataTypeName;
class MetadataDataContract {
constructor(init) { Object.assign(this, init); }
}
exports.MetadataDataContract = MetadataDataContract;
class MetadataDataMember {
constructor(init) { Object.assign(this, init); }
}
exports.MetadataDataMember = MetadataDataMember;
class MetadataAttribute {
constructor(init) { Object.assign(this, init); }
}
exports.MetadataAttribute = MetadataAttribute;
class MetadataPropertyType {
constructor(init) { Object.assign(this, init); }
}
exports.MetadataPropertyType = MetadataPropertyType;
class MetadataType {
constructor(init) { Object.assign(this, init); }
}
exports.MetadataType = MetadataType;
class ImageInfo {
}
exports.ImageInfo = ImageInfo;
class InputInfo {
}
exports.InputInfo = InputInfo;
class FormatInfo {
}
exports.FormatInfo = FormatInfo;
class RefInfo {
}
exports.RefInfo = RefInfo;
class KeyValuePair {
}
exports.KeyValuePair = KeyValuePair;
class FieldCss {
}
exports.FieldCss = FieldCss;
class NewInstanceResolver {
tryResolve(ctor) {
return new ctor();
}
}
exports.NewInstanceResolver = NewInstanceResolver;
class SingletonInstanceResolver {
tryResolve(ctor) {
return ctor.instance
|| (ctor.instance = new ctor());
}
}
exports.SingletonInstanceResolver = SingletonInstanceResolver;
function eventMessageType(evt) {
switch (evt) {
case 'onConnect':
return 'ServerEventConnect';
case 'onHeartbeat':
return 'ServerEventHeartbeat';
case 'onJoin':
return 'ServerEventJoin';
case 'onLeave':
return 'ServerEventLeave';
case 'onUpdate':
return 'ServerEventUpdate';
}
return null;
}
/**
* EventSource
*/
var ReadyState;
(function (ReadyState) {
ReadyState[ReadyState["CONNECTING"] = 0] = "CONNECTING";
ReadyState[ReadyState["OPEN"] = 1] = "OPEN";
ReadyState[ReadyState["CLOSED"] = 2] = "CLOSED";
})(ReadyState = exports.ReadyState || (exports.ReadyState = {}));
class ServerEventsClient {
constructor(baseUrl, channels, options = {}, eventSource = null) {
this.channels = channels;
this.options = options;
this.eventSource = eventSource;
this.onMessage = (e) => {
if (typeof document == "undefined") { //node
//latest node-fetch + eventsource doesn't split SSE messages properly
let requireSplitPos = e.data ? e.data.indexOf('\n') : -1;
if (requireSplitPos >= 0) {
let data = e.data;
let lastEventId = e.lastEventId;
let e1 = Object.assign({}, { lastEventId, data: data.substring(0, requireSplitPos) }), e2 = Object.assign({}, { lastEventId, data: data.substring(requireSplitPos + 1) });
this._onMessage(e1);
this._onMessage(e2);
return;
}
}
this._onMessage(e);
};
this._onMessage = (e) => {
if (this.stopped)
return;
let opt = this.options;
if (typeof document == "undefined") {
var document = {
querySelectorAll: sel => []
};
}
let parts = splitOnFirst(e.data, " ");
let channel = null;
let selector = parts[0];
let selParts = splitOnFirst(selector, "@");
if (selParts.length > 1) {
channel = selParts[0];
selector = selParts[1];
}
const json = parts[1];
let body = null;
try {
body = json ? JSON.parse(json) : null;
}
catch (ignore) { }
parts = splitOnFirst(selector, ".");
if (parts.length <= 1)
throw "invalid selector format: " + selector;
let op = parts[0], target = parts[1].replace(new RegExp("%20", "g"), " ");
const tokens = splitOnFirst(target, "$");
const [cmd, cssSelector] = tokens;
const els = cssSelector && $$(cssSelector);
const el = els && els[0];
const eventId = parseInt(e.lastEventId);
const data = e.data;
const type = eventMessageType(cmd) || "ServerEventMessage";
const request = { eventId, data, type,
channel, selector, json, body, op, target: tokens[0], cssSelector, meta: {} };
const mergedBody = typeof body == "object"
? Object.assign({}, request, body)
: request;
if (opt.validate && opt.validate(request) === false)
return;
let headers = new Headers();
headers.set("Content-Type", "text/plain");
if (op === "cmd") {
if (cmd === "onConnect") {
this.connectionInfo = mergedBody;
if (typeof body.heartbeatIntervalMs == "string")
this.connectionInfo.heartbeatIntervalMs = parseInt(body.heartbeatIntervalMs);
if (typeof body.idleTimeoutMs == "string")
this.connectionInfo.idleTimeoutMs = parseInt(body.idleTimeoutMs);
Object.assign(opt, body);
let fn = opt.handlers["onConnect"];
if (fn) {
fn.call(el || document.body, this.connectionInfo, request);
if (this.stopped)
return;
}
if (opt.heartbeatUrl) {
if (opt.heartbeat) {
clearInterval(opt.heartbeat);
}
opt.heartbeat = setInterval(() => __awaiter(this, void 0, void 0, function* () {
if (this.eventSource.readyState === EventSource.CLOSED) {
clearInterval(opt.heartbeat);
const stopFn = opt.handlers["onStop"];
if (stopFn != null)
stopFn.apply(this.eventSource);
this.reconnectServerEvents({ error: new Error("EventSource is CLOSED") });
return;
}
const reqHeartbeat = new Request(opt.heartbeatUrl, {
method: "POST", mode: "cors", headers: headers, credentials: this.serviceClient.credentials
});
try {
let res = yield fetch(reqHeartbeat);
if (!res.ok) {
const error = new Error(`${res.status} - ${res.statusText}`);
this.reconnectServerEvents({ error });
}
else {
yield res.text();
}
}
catch (error) {
this.reconnectServerEvents({ error });
}
}), (this.connectionInfo && this.connectionInfo.heartbeatIntervalMs) || opt.heartbeatIntervalMs || 10000);
}
if (opt.unRegisterUrl) {
if (typeof window != "undefined") {
window.onunload = () => {
if (navigator.sendBeacon) { // Chrome https://developers.google.com/web/updates/2019/12/chrome-80-deps-rems
this.stopped = true;
if (this.eventSource)
this.eventSource.close();
navigator.sendBeacon(opt.unRegisterUrl);
}
else {
this.stop();
}
};
}
}
this.updateSubscriberUrl = opt.updateSubscriberUrl;
this.updateChannels((opt.channels || "").split(","));
}
else {
let isCmdMsg = cmd == "onJoin" || cmd == "onLeave" || cmd == "onUpdate";
let fn = opt.handlers[cmd];
if (fn) {
if (isCmdMsg) {
fn.call(el || document.body, mergedBody);
}
else {
fn.call(el || document.body, body, request);
}
}
else {
if (!isCmdMsg) { //global receiver
let r = opt.receivers && opt.receivers["cmd"];
this.invokeReceiver(r, cmd, el, request, "cmd");
}
}
if (isCmdMsg) {
fn = opt.handlers["onCommand"];
if (fn) {
fn.call(el || document.body, mergedBody);
}
}
}
}
else if (op === "trigger") {
this.raiseEvent(target, request);
}
else if (op === "css") {
css(els || $$("body"), cmd, body);
}
//Named Receiver
let r = opt.receivers && opt.receivers[op];
this.invokeReceiver(r, cmd, el, request, op);
if (!eventMessageType(cmd)) {
let fn = opt.handlers["onMessage"];
if (fn) {
fn.call(el || document.body, mergedBody);
}
}
if (opt.onTick)
opt.onTick();
};
this.onError = (error) => {
if (this.stopped)
return;
if (!error)
error = event;
let fn = this.options.onException;
if (fn != null)
fn.call(this.eventSource, error);
if (this.options.onTick)
this.options.onTick();
};
if (this.channels.length === 0)
throw "at least 1 channel is required";
this.resolver = this.options.resolver || new NewInstanceResolver();
this.eventStreamUri = combinePaths(baseUrl, "event-stream") + "?";
this.updateChannels(channels);
this.serviceClient = new JsonServiceClient(baseUrl);
this.listeners = {};
this.withCredentials = true;
if (!this.options.handlers)
this.options.handlers = {};
}
getEventSourceOptions() {
return { withCredentials: this.withCredentials };
}
reconnectServerEvents(opt = {}) {
if (this.stopped)
return;
if (opt.error)
this.onError(opt.error);
const hold = this.eventSource;
let url = opt.url || this.eventStreamUri || hold.url;
if (this.options.resolveStreamUrl != null) {
url = this.options.resolveStreamUrl(url);
}
const es = this.EventSource
? new this.EventSource(url, this.getEventSourceOptions())
: new EventSource(url, this.getEventSourceOptions());
es.addEventListener('error', e => (opt.onerror || hold.onerror || this.onError)(e));
es.addEventListener('message', opt.onmessage || hold.onmessage || this.onMessage);
let fn = this.options.onReconnect;
if (fn != null)
fn.call(es, opt.error);
if (hold.removeEventListener) {
hold.removeEventListener('error', this.onError);
hold.removeEventListener('message', this.onMessage);
}
hold.close();
return this.eventSource = es;
}
start() {
this.stopped = false;
if (this.eventSource == null || this.eventSource.readyState === EventSource.CLOSED) {
let url = this.eventStreamUri;
if (this.options.resolveStreamUrl != null) {
url = this.options.resolveStreamUrl(url);
}
this.eventSource = this.EventSource
? new this.EventSource(url, this.getEventSourceOptions())
: new EventSource(url, this.getEventSourceOptions());
this.eventSource.addEventListener('error', this.onError);
this.eventSource.addEventListener('message', e => this.onMessage(e));
}
return this;
}
stop() {
this.stopped = true;
if (this.eventSource) {
this.eventSource.close();
}
let opt = this.options;
if (opt && opt.heartbeat) {
clearInterval(opt.heartbeat);
}
let hold = this.connectionInfo;
if (hold == null || hold.unRegisterUrl == null)
return new Promise((resolve, reject) => resolve());
this.connectionInfo = null;
return fetch(new Request(hold.unRegisterUrl, { method: "POST", mode: "cors", credentials: this.serviceClient.credentials }))
.then(res => { if (!res.ok)
throw new Error(`${res.status} - ${res.statusText}`); })
.catch(this.onError);
}
invokeReceiver(r, cmd, el, request, name) {
if (r) {
if (typeof r == "function") {
r = this.resolver.tryResolve(r);
}
cmd = cmd.replace("-", "");
r.client = this;
r.request = request;
if (typeof (r[cmd]) == "function") {
r[cmd].call(el || r, request.body, request);
}
else if (cmd in r) {
r[cmd] = request.body;
}
else {
let metaProp = Object.getOwnPropertyDescriptor(r, cmd);
if (metaProp != null) {
if (metaProp.set) {
metaProp.set(request.body);
}
else if (metaProp.writable) {
r[cmd] = request.body;
}
return;
}
let cmdLower = cmd.toLowerCase();
getAllMembers(r).forEach(k => {
if (k.toLowerCase() == cmdLower) {
if (typeof r[k] == "function") {
r[k].call(el || r, request.body, request);
}
else {
r[k] = request.body;
}
return;
}
});
let noSuchMethod = r["noSuchMethod"];
if (typeof noSuchMethod == "function") {
noSuchMethod.call(el || r, request.target, request);
}
}
}
}
hasConnected() {
return this.connectionInfo != null;
}
registerHandler(name, fn) {
if (!this.options.handlers)
this.options.handlers = {};
this.options.handlers[name] = fn;
return this;
}
setResolver(resolver) {
this.options.resolver = resolver;
return this;
}
registerReceiver(receiver) {
return this.registerNamedReceiver("cmd", receiver);
}
registerNamedReceiver(name, receiver) {
if (!this.options.receivers)
this.options.receivers = {};
this.options.receivers[name] = receiver;
return this;
}
unregisterReceiver(name = "cmd") {
if (this.options.receivers) {
delete this.options.receivers[name];
}
return this;
}
updateChannels(channels) {
this.channels = channels;
const url = this.eventSource != null
? this.eventSource.url
: this.eventStreamUri;
this.eventStreamUri = url.substring(0, Math.min(url.indexOf("?"), url.length)) + "?channels=" + channels.join(",") + "&t=" + new Date().getTime();
}
update(subscribe, unsubscribe) {
let sub = typeof subscribe == "string" ? subscribe.split(',') : subscribe;
let unsub = typeof unsubscribe == "string" ? unsubscribe.split(',') : unsubscribe;
let channels = [];
for (let i in this.channels) {
let c = this.channels[i];
if (unsub == null || unsub.indexOf(c) === -1) {
channels.push(c);
}
}
if (sub) {
for (let i in sub) {
let c = sub[i];
if (channels.indexOf(c) === -1) {
channels.push(c);
}
}
}
this.updateChannels(channels);
}
addListener(eventName, handler) {
let handlers = this.listeners[eventName] || (this.listeners[eventName] = []);
handlers.push(handler);
return this;
}
removeListener(eventName, handler) {
let handlers = this.listeners[eventName];
if (handlers) {
let pos = handlers.indexOf(handler);
if (pos >= 0) {
handlers.splice(pos, 1);
}
}
return this;
}
raiseEvent(eventName, msg) {
let handlers = this.listeners[eventName];
if (handlers) {
handlers.forEach(x => {
try {
x(msg);
}
catch (e) {
this.onError(e);
}
});
}
}
getConnectionInfo() {
if (this.connectionInfo == null)
throw "Not Connected";
return this.connectionInfo;
}
getSubscriptionId() {
return this.getConnectionInfo().id;
}
updateSubscriber(request) {
if (request.id == null)
request.id = this.getSubscriptionId();
return this.serviceClient.post(request)
.then(x => {
this.update(request.subscribeChannels, request.unsubscribeChannels);
}).catch(this.onError);
}
subscribeToChannels(...channels) {
let request = new UpdateEventSubscriber();
request.id = this.getSubscriptionId();
request.subscribeChannels = channels;
return this.serviceClient.post(request)
.then(x => {
this.update(channels, null);
}).catch(this.onError);
}
unsubscribeFromChannels(...channels) {
let request = new UpdateEventSubscriber();
request.id = this.getSubscriptionId();
request.unsubscribeChannels = channels;
return this.serviceClient.post(request)
.then(x => {
this.update(null, channels);
}).catch(this.onError);
}
getChannelSubscribers() {
let request = new GetEventSubscribers();
request.channels = this.channels;
return this.serviceClient.get(request)
.then(r => r.map(x => this.toServerEventUser(x)))
.catch(e => {
this.onError(e);
return [];
});
}
toServerEventUser(map) {
let channels = map["channels"];
let to = new ServerEventUser();
to.userId = map["userId"];
to.displayName = map["displayName"];
to.profileUrl = map["profileUrl"];
to.channels = channels ? channels.split(',') : null;
for (let k in map) {
if (k == "userId" || k == "displayName" ||
k == "profileUrl" || k == "channels")
continue;
if (to.meta == null)
to.meta = {};
to.meta[k] = map[k];
}
return to;
}
}
exports.ServerEventsClient = ServerEventsClient;
ServerEventsClient.UnknownChannel = "*";
function getAllMembers(o) {
let props = [];
do {
const l = Object.getOwnPropertyNames(o)
.concat(Object.getOwnPropertySymbols(o).map(s => s.toString()))
.sort()
.filter((p, i, arr) => p !== 'constructor' && //not the constructor
(i == 0 || p !== arr[i - 1]) && //not overriding in this prototype
props.indexOf(p) === -1 //not overridden in a child
);
props = props.concat(l);
} while ((o = Object.getPrototypeOf(o)) && //walk-up the prototype chain
Object.getPrototypeOf(o) //not the the Object prototype methods (hasOwnProperty, etc...)
);
return props;
}
exports.getAllMembers = getAllMembers;
class ServerEventReceiver {
noSuchMethod(selector, message) { }
}
exports.ServerEventReceiver = ServerEventReceiver;
class UpdateEventSubscriber {
createResponse() { return new UpdateEventSubscriberResponse(); }
getTypeName() { return "UpdateEventSubscriber"; }
}
exports.UpdateEventSubscriber = UpdateEventSubscriber;
class UpdateEventSubscriberResponse {
}
exports.UpdateEventSubscriberResponse = UpdateEventSubscriberResponse;
class GetEventSubscribers {
createResponse() { return []; }
getTypeName() { return "GetEventSubscribers"; }
}
exports.GetEventSubscribers = GetEventSubscribers;
class ServerEventUser {
}
exports.ServerEventUser = ServerEventUser;
class HttpMethods {
}
exports.HttpMethods = HttpMethods;
HttpMethods.Get = "GET";
HttpMethods.Post = "POST";
HttpMethods.Put = "PUT";
HttpMethods.Delete = "DELETE";
HttpMethods.Patch = "PATCH";
HttpMethods.Head = "HEAD";
HttpMethods.Options = "OPTIONS";
HttpMethods.hasRequestBody = (method) => !(method === "GET" || method === "DELETE" || method === "HEAD" || method === "OPTIONS");
class GetAccessToken {
constructor(init) { Object.assign(this, init); }
createResponse() { return new GetAccessTokenResponse(); }
getTypeName() { return 'GetAccessToken'; }
getMethod() { return 'POST'; }
}
class GetAccessTokenResponse {
}
exports.GetAccessTokenResponse = GetAccessTokenResponse;
class JsonServiceClient {
constructor(baseUrl = "/") {
this.baseUrl = baseUrl;
this.mode = "cors";
this.credentials = "include";
this.headers = new Headers();
this.headers.set("Content-Type", "application/json");
this.manageCookies = typeof document == "undefined"; //because node-fetch doesn't
this.cookies = {};
this.enableAutoRefreshToken = true;
this.basePath = 'api';
}
setCredentials(userName, password) {
this.userName = userName;
this.password = password;
}
useBasePath(path) {
this.basePath = path;
return this;
}
set basePath(path) {
if (!path) {
this.replyBaseUrl = combinePaths(this.baseUrl, "json", "reply") + "/";
this.oneWayBaseUrl = combinePaths(this.baseUrl, "json", "oneway") + "/";
}
else {
this.replyBaseUrl = combinePaths(this.baseUrl, path) + "/";
this.oneWayBaseUrl = combinePaths(this.baseUrl, path) + "/";
}
}
apply(f) {
f(this);
return this;
}
get(request, args) {
return typeof request != "string"
? this.fetch(HttpMethods.Get, request, args)
: this.fetch(HttpMethods.Get, null, args, this.toAbsoluteUrl(request));
}
delete(request, args) {
return typeof request != "string"
? this.fetch(HttpMethods.Delete, request, args)
: this.fetch(HttpMethods.Delete, null, args, this.toAbsoluteUrl(request));
}
post(request, args) {
return this.fetch(HttpMethods.Post, request, args);
}
postToUrl(url, request, args) {
return this.fetch(HttpMethods.Post, request, args, this.toAbsoluteUrl(url));
}
postBody(request, body, args) {
return this.fetchBody(HttpMethods.Post, request, body, args);
}
put(request, args) {
return this.fetch(HttpMethods.Put, request, args);
}
putToUrl(url, request, args) {
return this.fetch(HttpMethods.Put, request, args, this.toAbsoluteUrl(url));
}
putBody(request, body, args) {
return this.fetchBody(HttpMethods.Put, request, body, args);
}
patch(request, args) {
return this.fetch(HttpMethods.Patch, request, args);
}
patchToUrl(url, request, args) {
return this.fetch(HttpMethods.Patch, request, args, this.toAbsoluteUrl(url));
}
patchBody(request, body, args) {
return this.fetchBody(HttpMethods.Patch, request, body, args);
}
publish(request, args) {
return this.sendOneWay(request, args);
}
sendOneWay(request, args) {
const url = combinePaths(this.oneWayBaseUrl, nameOf(request));
return this.fetch(HttpMethods.Post, request, null, url);
}
sendAll(requests) {
if (requests.length == 0)
return Promise.resolve([]);
const url = combinePaths(this.replyBaseUrl, nameOf(requests[0]) + "[]");
return this.fetch(HttpMethods.Post, requests, null, url);
}
sendAllOneWay(requests) {
if (requests.length == 0)
return Promise.resolve(void 0);
const url = combinePaths(this.oneWayBaseUrl, nameOf(requests[0]) + "[]");
return this.fetch(HttpMethods.Post, requests, null, url)
.then(r => void 0);
}
createUrlFromDto(method, request) {
let url = combinePaths(this.replyBaseUrl, nameOf(request));
const hasRequestBody = HttpMethods.hasRequestBody(method);
if (!hasRequestBody)
url = appendQueryString(url, request);
return url;
}
toAbsoluteUrl(relativeOrAbsoluteUrl) {
return relativeOrAbsoluteUrl.startsWith("http://") ||
relativeOrAbsoluteUrl.startsWith("https://")
? relativeOrAbsoluteUrl
: combinePaths(this.baseUrl, relativeOrAbsoluteUrl);
}
deleteCookie(name) {
if (this.manageCookies) {
delete this.cookies[name];
}
else {
if (document) {
document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/';
}
}
}
createRequest({ method, request, url, args, body }) {
if (!url)
url = this.createUrlFromDto(method, request);
if (args)
url = appendQueryString(url, args);
if (this.bearerToken != null) {
this.headers.set("Authorization", "Bearer " + this.bearerToken);
}
else if (this.userName != null) {
this.headers.set('Authorization', 'Basic ' + JsonServiceClient.toBase64(`${this.userName}:${this.password}`));
}
if (this.manageCookies) {
let cookies = Object.keys(this.cookies)
.map(x => {
let c = this.cookies[x];
return c.expires && c.expires < new Date()
? null
: `${c.name}=${encodeURIComponent(c.value)}`;
})
.filter(x => !!x);
if (cookies.length > 0)
this.headers.set("Cookie", cookies.join("; "));
else
this.headers.delete("Cookie");
}
let headers = new Headers(this.headers);
let hasRequestBody = HttpMethods.hasRequestBody(method);
let reqInit = {
url,
method: method,
mode: this.mode,
credentials: this.credentials,
headers,
compress: false, // https://github.com/bitinn/node-fetch/issues/93#issuecomment-200791658
};
if (hasRequestBody) {
reqInit.body = body || JSON.stringify(request);
if (isFormData(body)) {
reqInit.body = sanitizeFormData(body);
headers.delete('Content-Type'); //set by FormData
}
}
if (this.requestFilter != null)
this.requestFilter(reqInit);
if (JsonServiceClient.globalRequestFilter != null)
JsonServiceClient.globalRequestFilter(reqInit);
return reqInit;
}
json(res) {
if (this.parseJson)
return this.parseJson(res);
return res.text().then(txt => {
return txt.length > 0 ? JSON.parse(txt) : null;
});
}
applyResponseFilters(res) {
if (this.responseFilter != null)
this.responseFilter(res);
if (JsonServiceClient.globalResponseFilter != null)
JsonServiceClient.globalResponseFilter(res);
}
createResponse(res, request) {
if (!res.ok) {
this.applyResponseFilters(res);
throw res;
}
if (this.manageCookies) {
let setCookies = [];
res.headers.forEach((v, k) => {
switch (k.toLowerCase()) {
case "set-cookie":
let cookies = v.split(',');
cookies.forEach(c => setCookies.push(c));
break;
}
});
setCookies.forEach(x => {
let cookie = parseCookie(x);
if (cookie)
this.cookies[cookie.name] = cookie;
});
}
res.headers.forEach((v, k) => {
switch (k.toLowerCase()) {
case "x-cookies":
if (v.split(',').indexOf('ss-reftok') >= 0)
this.useTokenCookie = true;
break;
}
});
this.applyResponseFilters(res);
let x = request && typeof request != "string" && typeof request.createResponse == 'function'
? request.createResponse()
: null;
if (typeof x === 'string')
return res.text().then(o => o);
let contentType = res.headers.get("content-type");
let isJson = contentType && contentType.indexOf("application/json") !== -1;
if (isJson) {
return this.json(res).then(o => o);
}
if (typeof Uint8Array != "undefined" && x instanceof Uint8Array) {
if (typeof res.arrayBuffer != 'function')
throw new Error("This fetch polyfill does not implement 'arrayBuffer'");
return res.arrayBuffer().then(o => new Uint8Array(o));
}
else if (typeof Blob == "function" && x instanceof Blob) {
if (typeof res.blob != 'function')
throw new Error("This fetch polyfill does not implement 'blob'");
return res.blob().then(o => o);
}
let contentLength = res.headers.get("content-length");
if (contentLength === "0" || (contentLength == null && !isJson)) {
return res.text().then(_ => x);
}
return this.json(res).then(o => o); //fallback
}
handleError(holdRes, res, type = null) {
if (res instanceof Error)
throw this.raiseError(holdRes, res);
// res.json can only be called once.
if (res.bodyUsed)
throw this.raiseError(res, createErrorResponse(res.status, res.statusText, type));
let isErrorResponse = typeof res.json == "undefined" && res.responseStatus;
if (isErrorResponse) {
return new Promise((resolve, reject) => reject(this.raiseError(null, res)));
}
return this.json(res).then(o => {
let errorDto = sanitize(o);
if (!errorDto.responseStatus)
throw createErrorResponse(res.status, res.statusText, type);
if (type != null)
errorDto.type = type;
throw errorDto;
}).catch(error => {
// No responseStatus body, set from `res` Body object
if (error instanceof Error
|| (typeof window != "undefined" && window.DOMException && error instanceof window.DOMException /*MS Edge*/)) {
throw this.raiseError(res, createErrorResponse(res.status, res.statusText, type));
}
throw this.raiseError(res, error);
});
}
fetch(method, request, args, url) {
return this.sendRequest({ method, request, args, url });
}
fetchBody(method, request, body, args) {
let url = combinePaths(this.replyBaseUrl, nameOf(request));
return this.sendRequest({
method,
request: body,
body: typeof body == "string"
? body
: isFormData(body)
? body
: JSON.stringify(body),
url: appendQueryString(url, request),
args,
returns: request
});
}
sendRequest(info) {
const req = this.createRequest(info);
const returns = info.returns || info.request;
let holdRes = null;
const resendRequest = () => {
const req = this.createRequest(info);
if (this.urlFilter)
this.urlFilter(req.url);
return fetch(req.url, req)
.then(res => this.createResponse(res, returns))
.catch(res => this.handleError(holdRes, res));
};
if (this.urlFilter)
this.urlFilter(req.url);
return fetch(req.url, req)
.then(res => {
holdRes = res;
const response = this.createResponse(res, returns);
return response;
})
.catch(res => {
if (res.status === 401) {
if (this.enableAutoRefreshToken && (this.refreshToken || this.useTokenCookie || this.cookies['ss-reftok'] != null)) {
const jwtReq = new GetAccessToken({ refreshToken: this.refreshToken, useTokenCookie: !!this.useTokenCookie });
let url = this.refreshTokenUri || this.createUrlFromDto(HttpMethods.Post, jwtReq);
if (this.useTokenCookie) {
this.bearerToken = null;
this.headers.delete("Authorization");
}
let jwtRequest = this.createRequest({ method: HttpMethods.Post, request: jwtReq, args: null, url });
return fetch(url, jwtRequest)
.then(r => this.createResponse(r, jwtReq).then(jwtResponse => {
this.bearerToken = (jwtResponse === null || jwtResponse === void 0 ? void 0 : jwtResponse.accessToken) || null;
return resendRequest();
}))
.catch(res => {
if (this.onAuthenticationRequired) {
return this.onAuthenticationRequired()
.then(resendRequest)
.catch(resHandler => {
return this.handleError(holdRes, resHandler, "RefreshTokenException");
});
}
else {
return this.handleError(holdRes, res, "RefreshTokenException");
}
});
}
else {
if (this.onAuthenticationRequired) {
return this.onAuthenticationRequired().then(resendRequest);
}
}
}
return this.handleError(holdRes, res);
});
}
raiseError(res, error) {
if (this.exceptionFilter != null) {
this.exceptionFilter(res, error);
}
return error;
}
// Generic send that uses APIs preferred HTTP Method (requires v5.13+ DTOs)
send(request, args, url) {
return this.sendRequest({ method: getMethod(request), request, args, url });
}
// Generic send IReturnVoid that uses APIs preferred HTTP Method (requires v5.13+ DTOs)
sendVoid(request, args, url) {
return this.sendRequest({ method: getMethod(request), request, args, url });
}
api(request, args, method) {
return __awaiter(this, void 0, void 0, function* () {
try {
const result = yield this.fetch(getMethod(request, method), request, args);
return new ApiResult({ response: result });
}
catch (e) {
return new ApiResult({ error: getResponseStatus(e) });
}
});
}
apiVoid(request, args, method) {
return __awaiter(this, void 0, void 0, function* () {
try {
const result = yield this.fetch(getMethod(request, method), request, args);
return new ApiResult({ response: result !== null && result !== void 0 ? result : new EmptyResponse() });
}
catch (e) {
return new ApiResult({ error: getResponseStatus(e) });
}
});
}
apiForm(request, body, args, method) {
return __awaiter(this, void 0, void 0, function* () {
try {
const result = yield this.fetchBody(getMethod(request, method), request, body, args);
return new ApiResult({ response: result });
}
catch (e) {
return new ApiResult({ error: getResponseStatus(e) });
}
});
}
apiFormVoid(request, body, args, method) {
return __awaiter(this, void 0, void 0, function* () {
try {
const result = yield this.fetchBody(getMethod(request, method), request, body, args);
return new ApiResult({ response: result !== null && result !== void 0 ? result : new EmptyResponse() });
}
catch (e) {
return new ApiResult({ error: getResponseStatus(e) });
}
});
}
}
exports.JsonServiceClient = JsonServiceClient;
class JsonApiClient {
static create(baseUrl = "/", f) {
let client = new JsonServiceClient(baseUrl).apply(c => {
c.basePath = "/api";
c.headers = new Headers(); //avoid pre-flight CORS requests
c.enableAutoRefreshToken = false; // Use JWT Cookies by default
if (f) {
f(c);
}
});
return client;
}
}
exports.JsonApiClient = JsonApiClient;
function getMethod(request, method) {
return method !== null && method !== void 0 ? method : (typeof request.getMethod == "function"
? request.getMethod()
: HttpMethods.Post);
}
exports.getMethod = getMethod;
function getResponseStatus(e) {
var _a, _b;
return (_b = (_a = e.responseStatus) !== null && _a !== void 0 ? _a : e.ResponseStatus) !== null && _b !== void 0 ? _b : (e.errorCode
? e
: (e.message ? createErrorStatus(e.message, e.errorCode) : null));
}
exports.getResponseStatus = getResponseStatus;
class ApiResult {
constructor(init) { Object.assign(this, init); }
get completed() { return this.response != null || this.error != null; }
get failed() { var _a, _b; return ((_a = this.error) === null || _a === void 0 ? void 0 : _a.errorCode) != null || ((_b = this.error) === null || _b === void 0 ? void 0 : _b.message) != null; }
get succeeded() { return !this.failed && this.response != null; }
get errorMessage() { var _a; return (_a = this.error) === null || _a === void 0 ? void 0 : _a.message; }
get errorCode() { var _a; return (_a = this.error) === null || _a === void 0 ? void 0 : _a.errorCode; }
get errors() { var _a, _b; return (_b = (_a = this.error) === null || _a === void 0 ? void 0 : _a.errors) !== null && _b !== void 0 ? _b : []; }
get errorSummary() { return this.error != null && this.errors.length == 0 ? this.errorMessage : null; }
fieldError(fieldName) {
var _a;
let matchField = fieldName.toLowerCase();
return (_a = this.errors) === null || _a === void 0 ? void 0 : _a.find(x => x.fieldName.toLowerCase() == matchField);
}
fieldErrorMessage(fieldName) { var _a; return (_a = this.fieldError(fieldName)) === null || _a === void 0 ? void 0 : _a.message; }
hasFieldError(fieldName) { return this.fieldError(fieldName) != null; }
showSummary(exceptFields = []) {
if (!this.failed)
return false;
return exceptFields.every(x => !this.hasFieldError(x));
}
summaryMessage(exceptFields = []) {
if (this.showSummary(exceptFields)) {
// Return first field error that's not visible
let fieldSet = exceptFields.map(x => x.toLowerCase());
let fieldError = fieldSet.find(x => fieldSet.indexOf(x.toLowerCase()) == -1);
return fieldError !== null && fieldError !== void 0 ? fieldError : this.errorMessage;
}
}
addFieldError(fieldName, message, errorCode = 'Exception') {
if (!this.error)
this.error = new ResponseStatus();
const fieldError = this.fieldError(fieldName);
if (fieldError != null) {
fieldError.errorCode = errorCode;
fieldError.message = message;
}
else {
this.error.errors.push(new ResponseError({ fieldName, errorCode, message }));
}
}
}
exports.ApiResult = ApiResult;
function createErrorStatus(message, errorCode = 'Exception') {
return new ResponseStatus({ errorCode, message });
}
exports.createErrorStatus = createErrorStatus;
function createFieldError(fieldName, message, errorCode = 'Exception') {
return new ResponseStatus({ errors: [new ResponseError({ fieldName, errorCode, message })] });
}
exports.createFieldError = createFieldError;
function isFormData(body) { return body instanceof FormData; }
exports.isFormData = isFormData;
function createErrorResponse(errorCode, message, type = null) {
const error = apply(new ErrorResponse(), e => {
if (type != null)
e.type = type;
e.responseStatus = apply(new ResponseStatus(), status => {
status.errorCode = errorCode && errorCode.toString();
status.message = message;
});
});
return error;
}
function createError(errorCode, message, fieldName) {
return new ErrorResponse({
responseStatus: new ResponseStatus({
errorCode,
message,
errors: fieldName ? [new ResponseError({ errorCode, message, fieldName })] : undefined
})
});
}
exports.createError = createError;
function toPascalCase(s) {
if (!s)
return '';
const isAllCaps = s.match(/^[A-Z0-9_]+$/);
if (isAllCaps) {
const words = s.split('_');
return words.map(x => x[0].toUpperCase() + x.substring(1).toLowerCase()).join('');
}
if (s.includes('_')) {
return s.split('_').filter(x => x[0]).map(x => x[0].toUpperCase() + x.substring(1)).join('');
}
return s.charAt(0).toUpperCase() + s.substring(1);
}
exports.toPascalCase = toPascalCase;
function toCamelCase(s) {
s = toPascalCase(s);
if (!s)
return '';
return s.charAt(0).toLowerCase() + s.substring(1);
}
exports.toCamelCase = toCamelCase;
function toKebabCase(s) {
if (!s || s.length <= 1)
return s.toLowerCase();
return s
.replace(/([A-Z0-9])/g, '-$1')
.toLowerCase()
.replace(/^-/, '')
.replace(/-+/g, '-');
}
exports.toKebabCase = toKebabCase;
function map(o, f) { return o == null ? null : f(o); }
exports.map = map;
function camelCaseAny(o) {
if (!o || !(o instanceof Object) || Array.isArray(o))
return o;
let to = {};
for (let k in o) {
if (o.hasOwnProperty(k)) {
const key = toCamelCase(k);
const val = o[k];
if (Array.isArray(val))
to[key] = val.map(x => camelCaseAny(x));
else if (val instanceof Object)
to[key] = camelCaseAny(val);
else