@auxilium/datalynk-client 1.0.19 → 1.1.0-rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/datalynk-models.mjs +33 -84
- package/dist/api.d.ts +42 -18
- package/dist/api.d.ts.map +1 -1
- package/dist/auth.d.ts +48 -15
- package/dist/auth.d.ts.map +1 -1
- package/dist/files.d.ts +0 -1
- package/dist/files.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3513 -0
- package/dist/index.mjs +689 -112
- package/dist/login-prompt.d.ts +14 -8
- package/dist/login-prompt.d.ts.map +1 -1
- package/dist/meta.d.ts +464 -0
- package/dist/meta.d.ts.map +1 -0
- package/dist/service.worker.d.ts +2 -0
- package/dist/service.worker.d.ts.map +1 -0
- package/dist/service.worker.js +15 -0
- package/dist/service.worker.mjs +14 -0
- package/dist/slice.d.ts +92 -49
- package/dist/slice.d.ts.map +1 -1
- package/dist/themes.d.ts +24 -0
- package/dist/themes.d.ts.map +1 -0
- package/package.json +3 -3
- package/dist/index.cjs +0 -2940
package/dist/index.mjs
CHANGED
|
@@ -15,6 +15,31 @@ function clean(obj, undefinedOnly = false) {
|
|
|
15
15
|
}
|
|
16
16
|
return obj;
|
|
17
17
|
}
|
|
18
|
+
function deepCopy(value) {
|
|
19
|
+
try {
|
|
20
|
+
return structuredClone(value);
|
|
21
|
+
} catch {
|
|
22
|
+
return JSON.parse(JSONSanitize(value));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function dotNotation(obj, prop, set) {
|
|
26
|
+
if (obj == null || !prop) return void 0;
|
|
27
|
+
return prop.split(/[.[\]]/g).filter((prop2) => prop2.length).reduce((obj2, prop2, i, arr) => {
|
|
28
|
+
if (prop2[0] == '"' || prop2[0] == "'") prop2 = prop2.slice(1, -1);
|
|
29
|
+
if (!(obj2 == null ? void 0 : obj2.hasOwnProperty(prop2))) {
|
|
30
|
+
return void 0;
|
|
31
|
+
}
|
|
32
|
+
return obj2[prop2];
|
|
33
|
+
}, obj);
|
|
34
|
+
}
|
|
35
|
+
function isEqual(a, b) {
|
|
36
|
+
const ta = typeof a, tb = typeof b;
|
|
37
|
+
if (ta != "object" || a == null || (tb != "object" || b == null))
|
|
38
|
+
return ta == "function" && tb == "function" ? a.toString() == b.toString() : a === b;
|
|
39
|
+
const keys = Object.keys(a);
|
|
40
|
+
if (keys.length != Object.keys(b).length) return false;
|
|
41
|
+
return Object.keys(a).every((key) => isEqual(a[key], b[key]));
|
|
42
|
+
}
|
|
18
43
|
function JSONAttemptParse(json) {
|
|
19
44
|
try {
|
|
20
45
|
return JSON.parse(json);
|
|
@@ -32,6 +57,214 @@ function JSONSanitize(obj, space) {
|
|
|
32
57
|
return value;
|
|
33
58
|
}, space);
|
|
34
59
|
}
|
|
60
|
+
class ASet extends Array {
|
|
61
|
+
/** Number of elements in set */
|
|
62
|
+
get size() {
|
|
63
|
+
return this.length;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Array to create set from, duplicate values will be removed
|
|
67
|
+
* @param {T[]} elements Elements which will be added to set
|
|
68
|
+
*/
|
|
69
|
+
constructor(elements = []) {
|
|
70
|
+
super();
|
|
71
|
+
if (!!(elements == null ? void 0 : elements["forEach"]))
|
|
72
|
+
elements.forEach((el) => this.add(el));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Add elements to set if unique
|
|
76
|
+
* @param items
|
|
77
|
+
*/
|
|
78
|
+
add(...items) {
|
|
79
|
+
items.filter((el) => !this.has(el)).forEach((el) => this.push(el));
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Remove all elements
|
|
84
|
+
*/
|
|
85
|
+
clear() {
|
|
86
|
+
this.splice(0, this.length);
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Delete elements from set
|
|
91
|
+
* @param items Elements that will be deleted
|
|
92
|
+
*/
|
|
93
|
+
delete(...items) {
|
|
94
|
+
items.forEach((el) => {
|
|
95
|
+
const index = this.indexOf(el);
|
|
96
|
+
if (index != -1) this.splice(index, 1);
|
|
97
|
+
});
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Create list of elements this set has which the comparison set does not
|
|
102
|
+
* @param {ASet<T>} set Set to compare against
|
|
103
|
+
* @return {ASet<T>} Different elements
|
|
104
|
+
*/
|
|
105
|
+
difference(set) {
|
|
106
|
+
return new ASet(this.filter((el) => !set.has(el)));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Check if set includes element
|
|
110
|
+
* @param {T} el Element to look for
|
|
111
|
+
* @return {boolean} True if element was found, false otherwise
|
|
112
|
+
*/
|
|
113
|
+
has(el) {
|
|
114
|
+
return this.indexOf(el) != -1;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Find index number of element, or -1 if it doesn't exist. Matches by equality not reference
|
|
118
|
+
*
|
|
119
|
+
* @param {T} search Element to find
|
|
120
|
+
* @param {number} fromIndex Starting index position
|
|
121
|
+
* @return {number} Element index number or -1 if missing
|
|
122
|
+
*/
|
|
123
|
+
indexOf(search2, fromIndex) {
|
|
124
|
+
return super.findIndex((el) => isEqual(el, search2), fromIndex);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Create list of elements this set has in common with the comparison set
|
|
128
|
+
* @param {ASet<T>} set Set to compare against
|
|
129
|
+
* @return {boolean} Set of common elements
|
|
130
|
+
*/
|
|
131
|
+
intersection(set) {
|
|
132
|
+
return new ASet(this.filter((el) => set.has(el)));
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if this set has no elements in common with the comparison set
|
|
136
|
+
* @param {ASet<T>} set Set to compare against
|
|
137
|
+
* @return {boolean} True if nothing in common, false otherwise
|
|
138
|
+
*/
|
|
139
|
+
isDisjointFrom(set) {
|
|
140
|
+
return this.intersection(set).size == 0;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Check if all elements in this set are included in the comparison set
|
|
144
|
+
* @param {ASet<T>} set Set to compare against
|
|
145
|
+
* @return {boolean} True if all elements are included, false otherwise
|
|
146
|
+
*/
|
|
147
|
+
isSubsetOf(set) {
|
|
148
|
+
return this.findIndex((el) => !set.has(el)) == -1;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Check if all elements from comparison set are included in this set
|
|
152
|
+
* @param {ASet<T>} set Set to compare against
|
|
153
|
+
* @return {boolean} True if all elements are included, false otherwise
|
|
154
|
+
*/
|
|
155
|
+
isSuperset(set) {
|
|
156
|
+
return set.findIndex((el) => !this.has(el)) == -1;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Create list of elements that are only in one set but not both (XOR)
|
|
160
|
+
* @param {ASet<T>} set Set to compare against
|
|
161
|
+
* @return {ASet<T>} New set of unique elements
|
|
162
|
+
*/
|
|
163
|
+
symmetricDifference(set) {
|
|
164
|
+
return new ASet([...this.difference(set), ...set.difference(this)]);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Create joined list of elements included in this & the comparison set
|
|
168
|
+
* @param {ASet<T>} set Set join
|
|
169
|
+
* @return {ASet<T>} New set of both previous sets combined
|
|
170
|
+
*/
|
|
171
|
+
union(set) {
|
|
172
|
+
return new ASet([...this, ...set]);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function sortByProp(prop, reverse = false) {
|
|
176
|
+
return function(a, b) {
|
|
177
|
+
const aVal = dotNotation(a, prop);
|
|
178
|
+
const bVal = dotNotation(b, prop);
|
|
179
|
+
if (typeof aVal == "number" && typeof bVal == "number")
|
|
180
|
+
return (reverse ? -1 : 1) * (aVal - bVal);
|
|
181
|
+
if (aVal > bVal) return reverse ? -1 : 1;
|
|
182
|
+
if (aVal < bVal) return reverse ? 1 : -1;
|
|
183
|
+
return 0;
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function makeArray(value) {
|
|
187
|
+
return Array.isArray(value) ? value : [value];
|
|
188
|
+
}
|
|
189
|
+
class Database {
|
|
190
|
+
constructor(database, tables, version2) {
|
|
191
|
+
__publicField2(this, "connection");
|
|
192
|
+
__publicField2(this, "tables");
|
|
193
|
+
this.database = database;
|
|
194
|
+
this.version = version2;
|
|
195
|
+
this.connection = new Promise((resolve, reject) => {
|
|
196
|
+
const req = indexedDB.open(this.database, this.version);
|
|
197
|
+
this.tables = tables.map((t) => {
|
|
198
|
+
t = typeof t == "object" ? t : { name: t };
|
|
199
|
+
return { ...t, name: t.name.toString() };
|
|
200
|
+
});
|
|
201
|
+
const tableNames = new ASet(this.tables.map((t) => t.name));
|
|
202
|
+
req.onerror = () => reject(req.error);
|
|
203
|
+
req.onsuccess = () => {
|
|
204
|
+
const db = req.result;
|
|
205
|
+
if (tableNames.symmetricDifference(new ASet(Array.from(db.objectStoreNames))).length) {
|
|
206
|
+
db.close();
|
|
207
|
+
Object.assign(this, new Database(this.database, this.tables, db.version + 1));
|
|
208
|
+
} else {
|
|
209
|
+
this.version = db.version;
|
|
210
|
+
resolve(db);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
req.onupgradeneeded = () => {
|
|
214
|
+
const db = req.result;
|
|
215
|
+
const existingTables = new ASet(Array.from(db.objectStoreNames));
|
|
216
|
+
existingTables.difference(tableNames).forEach((name) => db.deleteObjectStore(name));
|
|
217
|
+
tableNames.difference(existingTables).forEach((name) => db.createObjectStore(name));
|
|
218
|
+
};
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
includes(name) {
|
|
222
|
+
return !!this.tables.find((t) => t.name == name.toString());
|
|
223
|
+
}
|
|
224
|
+
table(name) {
|
|
225
|
+
return new Table(this, name.toString());
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
class Table {
|
|
229
|
+
constructor(database, name) {
|
|
230
|
+
this.database = database;
|
|
231
|
+
this.name = name;
|
|
232
|
+
}
|
|
233
|
+
async tx(table, fn2, readonly = false) {
|
|
234
|
+
const db = await this.database.connection;
|
|
235
|
+
const tx = db.transaction(table, readonly ? "readonly" : "readwrite");
|
|
236
|
+
const store = tx.objectStore(table);
|
|
237
|
+
return new Promise((resolve, reject) => {
|
|
238
|
+
const request = fn2(store);
|
|
239
|
+
request.onsuccess = () => resolve(request.result);
|
|
240
|
+
request.onerror = () => reject(request.error);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
add(value, key) {
|
|
244
|
+
return this.tx(this.name, (store) => store.add(value, key));
|
|
245
|
+
}
|
|
246
|
+
count() {
|
|
247
|
+
return this.tx(this.name, (store) => store.count(), true);
|
|
248
|
+
}
|
|
249
|
+
put(key, value) {
|
|
250
|
+
return this.tx(this.name, (store) => store.put(value, key));
|
|
251
|
+
}
|
|
252
|
+
getAll() {
|
|
253
|
+
return this.tx(this.name, (store) => store.getAll(), true);
|
|
254
|
+
}
|
|
255
|
+
getAllKeys() {
|
|
256
|
+
return this.tx(this.name, (store) => store.getAllKeys(), true);
|
|
257
|
+
}
|
|
258
|
+
get(key) {
|
|
259
|
+
return this.tx(this.name, (store) => store.get(key), true);
|
|
260
|
+
}
|
|
261
|
+
delete(key) {
|
|
262
|
+
return this.tx(this.name, (store) => store.delete(key));
|
|
263
|
+
}
|
|
264
|
+
clear() {
|
|
265
|
+
return this.tx(this.name, (store) => store.clear());
|
|
266
|
+
}
|
|
267
|
+
}
|
|
35
268
|
function contrast(background) {
|
|
36
269
|
const exploded = background == null ? void 0 : background.match(background.length >= 6 ? /[0-9a-fA-F]{2}/g : /[0-9a-fA-F]/g);
|
|
37
270
|
if (!exploded || (exploded == null ? void 0 : exploded.length) < 3) return "black";
|
|
@@ -1483,6 +1716,33 @@ var BehaviorSubject = function(_super) {
|
|
|
1483
1716
|
};
|
|
1484
1717
|
return BehaviorSubject2;
|
|
1485
1718
|
}(Subject);
|
|
1719
|
+
var EmptyError = createErrorClass(function(_super) {
|
|
1720
|
+
return function EmptyErrorImpl() {
|
|
1721
|
+
_super(this);
|
|
1722
|
+
this.name = "EmptyError";
|
|
1723
|
+
this.message = "no elements in sequence";
|
|
1724
|
+
};
|
|
1725
|
+
});
|
|
1726
|
+
function lastValueFrom(source, config2) {
|
|
1727
|
+
return new Promise(function(resolve, reject) {
|
|
1728
|
+
var _hasValue = false;
|
|
1729
|
+
var _value;
|
|
1730
|
+
source.subscribe({
|
|
1731
|
+
next: function(value) {
|
|
1732
|
+
_value = value;
|
|
1733
|
+
_hasValue = true;
|
|
1734
|
+
},
|
|
1735
|
+
error: reject,
|
|
1736
|
+
complete: function() {
|
|
1737
|
+
if (_hasValue) {
|
|
1738
|
+
resolve(_value);
|
|
1739
|
+
} else {
|
|
1740
|
+
reject(new EmptyError());
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
});
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1486
1746
|
function filter(predicate, thisArg) {
|
|
1487
1747
|
return operate(function(source, subscriber) {
|
|
1488
1748
|
var index = 0;
|
|
@@ -1512,6 +1772,34 @@ function distinctUntilChanged(comparator, keySelector) {
|
|
|
1512
1772
|
function defaultCompare(a, b) {
|
|
1513
1773
|
return a === b;
|
|
1514
1774
|
}
|
|
1775
|
+
function skip(count) {
|
|
1776
|
+
return filter(function(_, index) {
|
|
1777
|
+
return count <= index;
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
function takeWhile(predicate, inclusive) {
|
|
1781
|
+
return operate(function(source, subscriber) {
|
|
1782
|
+
var index = 0;
|
|
1783
|
+
source.subscribe(createOperatorSubscriber(subscriber, function(value) {
|
|
1784
|
+
var result = predicate(value, index++);
|
|
1785
|
+
(result || inclusive) && subscriber.next(value);
|
|
1786
|
+
!result && subscriber.complete();
|
|
1787
|
+
}));
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
async function getTheme(spoke, scope) {
|
|
1791
|
+
let theme = await fetch(`https://${spoke}.auxiliumgroup.com/api/js/auxilium/dijits/templates/login/${spoke}/theme.json`).then((res) => res.json()).catch(() => null);
|
|
1792
|
+
if (scope && theme != null && theme[scope]) theme = { ...theme, ...theme[scope] };
|
|
1793
|
+
return {
|
|
1794
|
+
found: !!theme,
|
|
1795
|
+
background: `https://${spoke}.auxiliumgroup.com/static/js/auxilium/dijits/templates/login/${spoke}/background.jpg`,
|
|
1796
|
+
color: "#c83232",
|
|
1797
|
+
logo: `https://${spoke}.auxiliumgroup.com/static/js/auxilium/dijits/templates/login/${spoke}/logo.png`,
|
|
1798
|
+
title: spoke.toUpperCase(),
|
|
1799
|
+
textColor: "white",
|
|
1800
|
+
...theme || {}
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1515
1803
|
const _LoginPrompt = class _LoginPrompt {
|
|
1516
1804
|
constructor(api, spoke, options = {}) {
|
|
1517
1805
|
__publicField(this, "alert");
|
|
@@ -1520,7 +1808,6 @@ const _LoginPrompt = class _LoginPrompt {
|
|
|
1520
1808
|
__publicField(this, "password");
|
|
1521
1809
|
__publicField(this, "persist");
|
|
1522
1810
|
__publicField(this, "username");
|
|
1523
|
-
__publicField(this, "options");
|
|
1524
1811
|
__publicField(this, "_done");
|
|
1525
1812
|
/** Promise which resolves once login is complete */
|
|
1526
1813
|
__publicField(this, "done", new Promise((res) => {
|
|
@@ -1528,13 +1815,10 @@ const _LoginPrompt = class _LoginPrompt {
|
|
|
1528
1815
|
}));
|
|
1529
1816
|
this.api = api;
|
|
1530
1817
|
this.spoke = spoke;
|
|
1531
|
-
this.options =
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
textColor: "#000000",
|
|
1536
|
-
...clean(options, true)
|
|
1537
|
-
};
|
|
1818
|
+
this.options = options;
|
|
1819
|
+
this.themeDefaults().then(() => this.render());
|
|
1820
|
+
}
|
|
1821
|
+
async render() {
|
|
1538
1822
|
this.close();
|
|
1539
1823
|
document.head.innerHTML += _LoginPrompt.css(this.options);
|
|
1540
1824
|
const div = document.createElement("div");
|
|
@@ -1546,9 +1830,17 @@ const _LoginPrompt = class _LoginPrompt {
|
|
|
1546
1830
|
this.password = document.querySelector('#datalynk-login-form input[name="password"]');
|
|
1547
1831
|
this.persist = document.querySelector('#datalynk-login-form input[name="persist"]');
|
|
1548
1832
|
this.username = document.querySelector('#datalynk-login-form input[name="username"]');
|
|
1549
|
-
if (options.persist === false) this.persist.parentElement.remove();
|
|
1833
|
+
if (this.options.persist === false) this.persist.parentElement.remove();
|
|
1550
1834
|
this.form.onsubmit = (event) => this.login(event);
|
|
1551
1835
|
}
|
|
1836
|
+
async themeDefaults() {
|
|
1837
|
+
const theme = await getTheme(this.spoke, "login");
|
|
1838
|
+
this.options = {
|
|
1839
|
+
logoOnly: !this.options.title && !theme.found,
|
|
1840
|
+
...theme,
|
|
1841
|
+
...clean(this.options, true)
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1552
1844
|
/** Close the login prompt */
|
|
1553
1845
|
close() {
|
|
1554
1846
|
var _a, _b;
|
|
@@ -1594,11 +1886,11 @@ __publicField(_LoginPrompt, "css", (options) => `
|
|
|
1594
1886
|
@import url('https://fonts.cdnfonts.com/css/ar-blanca');
|
|
1595
1887
|
|
|
1596
1888
|
#datalynk-login {
|
|
1597
|
-
--theme-background: ${options.background};
|
|
1889
|
+
--theme-background: ${options.backgroundColor ? options.backgroundColor : `url(${options.background})`};
|
|
1598
1890
|
--theme-container: #000000cc;
|
|
1599
1891
|
--theme-glow: ${options.glow || options.color};
|
|
1600
1892
|
--theme-primary: ${options.color};
|
|
1601
|
-
--theme-text: ${options.textColor}
|
|
1893
|
+
--theme-text: ${options.textColor};
|
|
1602
1894
|
|
|
1603
1895
|
position: fixed;
|
|
1604
1896
|
left: 0;
|
|
@@ -1611,7 +1903,7 @@ __publicField(_LoginPrompt, "css", (options) => `
|
|
|
1611
1903
|
font-family: sans-serif;
|
|
1612
1904
|
z-index: 1000;
|
|
1613
1905
|
}
|
|
1614
|
-
|
|
1906
|
+
|
|
1615
1907
|
#datalynk-login .added-links {
|
|
1616
1908
|
color: var(--theme-text);
|
|
1617
1909
|
position: fixed;
|
|
@@ -1747,12 +2039,18 @@ __publicField(_LoginPrompt, "css", (options) => `
|
|
|
1747
2039
|
/** Dynamically create HTML */
|
|
1748
2040
|
__publicField(_LoginPrompt, "template", (options) => `
|
|
1749
2041
|
<div id="datalynk-login">
|
|
2042
|
+
<!-- Used to check if image is valid -->
|
|
2043
|
+
${!options.backgroundColor ? `
|
|
2044
|
+
<img src="${options.background}" onerror="document.querySelector('#datalynk-login').style.backgroundImage = 'url(https://datalynk.auxiliumgroup.com/static/js/auxilium/dijits/templates/login/datalynk/background.jpg)'" style="width: 0; height: 0;"/>
|
|
2045
|
+
` : ""}
|
|
2046
|
+
|
|
1750
2047
|
<div class="added-links">
|
|
1751
2048
|
${(options.addLinks || []).map((link) => `<a href="${link.url || "#"}" target="_blank">${link.text}</a>`).join(" | ")}
|
|
1752
2049
|
</div>
|
|
1753
2050
|
<div class="login-container">
|
|
1754
2051
|
<div class="login-header">
|
|
1755
|
-
${options.title}
|
|
2052
|
+
${options.logo ? `<img alt="Logo" src="${options.logo}" class="login-logo" style="height: 100px; width: auto;" onerror="this.style.display='none'" ${options.logoOnly ? `onload="document.querySelector('.login-title').remove()"` : ""}>` : ""}
|
|
2053
|
+
<span class="login-title" style="margin-left: 0.5rem">${options.title}</span>
|
|
1756
2054
|
</div>
|
|
1757
2055
|
<div class="login-content">
|
|
1758
2056
|
<div class="login-body" style="max-width: 300px">
|
|
@@ -1798,13 +2096,17 @@ __publicField(_LoginPrompt, "template", (options) => `
|
|
|
1798
2096
|
let LoginPrompt = _LoginPrompt;
|
|
1799
2097
|
class Auth {
|
|
1800
2098
|
constructor(api) {
|
|
2099
|
+
__publicField(this, "onlinePrompt");
|
|
1801
2100
|
/** Current user as an observable */
|
|
1802
2101
|
__publicField(this, "user$", new BehaviorSubject(void 0));
|
|
2102
|
+
var _a;
|
|
1803
2103
|
this.api = api;
|
|
1804
2104
|
this.api.token$.subscribe(async (token) => {
|
|
1805
2105
|
if (token === void 0) return;
|
|
1806
2106
|
this.user = await this.current(token);
|
|
1807
2107
|
});
|
|
2108
|
+
if ((_a = this.api.options.offline) == null ? void 0 : _a.length)
|
|
2109
|
+
this.user$.pipe(filter((u) => u !== void 0)).subscribe((u) => localStorage.setItem("datalynk-user", JSON.stringify(u)));
|
|
1808
2110
|
}
|
|
1809
2111
|
/** Current user */
|
|
1810
2112
|
get user() {
|
|
@@ -1827,7 +2129,9 @@ class Auth {
|
|
|
1827
2129
|
var _a;
|
|
1828
2130
|
if (!token) return null;
|
|
1829
2131
|
else if (token == ((_a = this.user) == null ? void 0 : _a.token)) return this.user;
|
|
1830
|
-
|
|
2132
|
+
else if (typeof navigator != "undefined" && !navigator.onLine && typeof localStorage != "undefined" && localStorage.getItem("datalynk-user"))
|
|
2133
|
+
return JSON.parse(localStorage.getItem("datalynk-user"));
|
|
2134
|
+
return this.api.request([{ "$/auth/current": {} }, { "$/env/me": {} }], { token }).then((resp) => resp[0] || resp[1] ? { ...resp[0], ...resp[1] } : null);
|
|
1831
2135
|
}
|
|
1832
2136
|
/**
|
|
1833
2137
|
* Automatically handle sessions by checking localStorage & URL parameters for a token & prompting
|
|
@@ -1838,17 +2142,20 @@ class Auth {
|
|
|
1838
2142
|
* @return {Promise<void>} Login complete
|
|
1839
2143
|
*/
|
|
1840
2144
|
async handleLogin(spoke, options) {
|
|
1841
|
-
var _a;
|
|
2145
|
+
var _a, _b;
|
|
2146
|
+
if (this.onlinePrompt) window.removeEventListener("online", this.onlinePrompt);
|
|
2147
|
+
if ((_a = this.api.options.offline) == null ? void 0 : _a.length) window.removeEventListener("online", this.onlinePrompt = () => this.handleLogin(spoke, options));
|
|
1842
2148
|
const urlToken = new URLSearchParams(location.search).get("datalynkToken");
|
|
1843
2149
|
if (urlToken) {
|
|
1844
2150
|
this.api.token = urlToken;
|
|
1845
2151
|
location.href = location.href.replace(/datalynkToken=.*?(&|$)/gm, "");
|
|
1846
|
-
} else if (this.api.token) {
|
|
1847
|
-
if (((
|
|
2152
|
+
} else if (this.api.token && !this.api.expired) {
|
|
2153
|
+
if (((_b = this.api.jwtPayload) == null ? void 0 : _b.realm) != spoke) {
|
|
1848
2154
|
this.api.token = null;
|
|
1849
2155
|
location.reload();
|
|
1850
2156
|
}
|
|
1851
2157
|
} else {
|
|
2158
|
+
this.api.token = null;
|
|
1852
2159
|
await this.loginPrompt(spoke, options).done;
|
|
1853
2160
|
location.reload();
|
|
1854
2161
|
}
|
|
@@ -1953,6 +2260,7 @@ class Auth {
|
|
|
1953
2260
|
* @return {Promise<{closed: string, new: string}>}
|
|
1954
2261
|
*/
|
|
1955
2262
|
logout() {
|
|
2263
|
+
localStorage.removeItem("datalynk-user");
|
|
1956
2264
|
return this.api.request({ "$/auth/logout": {} }).then((resp) => {
|
|
1957
2265
|
this.api.token = null;
|
|
1958
2266
|
return resp;
|
|
@@ -1967,11 +2275,13 @@ class Auth {
|
|
|
1967
2275
|
* @return {Promise<any>} New session
|
|
1968
2276
|
*/
|
|
1969
2277
|
reset(login, newPassword, code) {
|
|
1970
|
-
return this.api.request({
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2278
|
+
return this.api.request({
|
|
2279
|
+
"$/auth/mobile/rescue": {
|
|
2280
|
+
user: login,
|
|
2281
|
+
password: newPassword,
|
|
2282
|
+
pin: code
|
|
2283
|
+
}
|
|
2284
|
+
}).then((resp) => {
|
|
1975
2285
|
if (resp.token) this.api.token = resp.token;
|
|
1976
2286
|
return resp;
|
|
1977
2287
|
});
|
|
@@ -1990,9 +2300,7 @@ class Auth {
|
|
|
1990
2300
|
}
|
|
1991
2301
|
class Files {
|
|
1992
2302
|
constructor(api) {
|
|
1993
|
-
__publicField(this, "url");
|
|
1994
2303
|
this.api = api;
|
|
1995
|
-
this.url = `${this.api.url}file`;
|
|
1996
2304
|
}
|
|
1997
2305
|
associate(fileIds, slice, row, field, execute = true) {
|
|
1998
2306
|
const req = { [`${execute ? "!" : "$"}/tools/file/update`]: { slice, row, field, ids: fileIds } };
|
|
@@ -2007,7 +2315,7 @@ class Files {
|
|
|
2007
2315
|
* @return {string} URL file can be viewed at
|
|
2008
2316
|
*/
|
|
2009
2317
|
get(id, ignoreToken) {
|
|
2010
|
-
return `${this.url}?id=${id}${ignoreToken ? "" : `&token=${this.api.token}`}`;
|
|
2318
|
+
return `${this.api.url}file?id=${id}${ignoreToken ? "" : `&token=${this.api.token}`}`;
|
|
2011
2319
|
}
|
|
2012
2320
|
/**
|
|
2013
2321
|
* Upload file(s) to the API & optionally associate them with a row
|
|
@@ -2016,25 +2324,23 @@ class Files {
|
|
|
2016
2324
|
* @param {{slice: number, row: any, field: string, pk?: string}} associate Row to associate with
|
|
2017
2325
|
*/
|
|
2018
2326
|
upload(files, associate) {
|
|
2019
|
-
let f =
|
|
2020
|
-
if (files instanceof FileList) f = Array.from(files);
|
|
2021
|
-
else f = Array.isArray(files) ? files : [files];
|
|
2327
|
+
let f = files instanceof FileList ? Array.from(files) : makeArray(files);
|
|
2022
2328
|
return Promise.all(f.map((file) => {
|
|
2023
2329
|
const data = new FormData();
|
|
2024
|
-
data.append("", file, file.name);
|
|
2025
|
-
return fetch(this.url
|
|
2330
|
+
data.append("uploadedfiles[]", file, file.name);
|
|
2331
|
+
return fetch(`${this.api.url}upload.php`, {
|
|
2026
2332
|
method: "POST",
|
|
2027
2333
|
headers: clean({ "Authorization": this.api.token ? `Bearer ${this.api.token}` : "" }),
|
|
2028
2334
|
body: data
|
|
2029
2335
|
}).then(async (resp) => {
|
|
2030
2336
|
const data2 = await resp.json().catch(() => ({}));
|
|
2031
2337
|
if (!resp.ok || data2["error"]) throw Object.assign(errorFromCode(resp.status, data2.error) || {}, data2);
|
|
2032
|
-
return
|
|
2338
|
+
return data2.files.uploadedfiles[0];
|
|
2033
2339
|
});
|
|
2034
2340
|
})).then(async (files2) => {
|
|
2035
2341
|
if (associate) {
|
|
2036
2342
|
let id = typeof associate.row == "number" ? associate.row : associate.row[associate.pk || "id"];
|
|
2037
|
-
if (!id) id = await this.api.slice(associate.slice).insert(associate.row).id();
|
|
2343
|
+
if (!id) id = await this.api.slice(associate.slice).insert(associate.row).id().exec();
|
|
2038
2344
|
await this.associate(files2.map((f2) => f2.id), associate == null ? void 0 : associate.slice, associate == null ? void 0 : associate.row, associate == null ? void 0 : associate.field);
|
|
2039
2345
|
}
|
|
2040
2346
|
return files2;
|
|
@@ -2194,23 +2500,13 @@ const Serializer = {
|
|
|
2194
2500
|
}
|
|
2195
2501
|
}
|
|
2196
2502
|
};
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
* An object to aid in constructing requests
|
|
2200
|
-
*
|
|
2201
|
-
* @param {number} slice Slice ID to interact with
|
|
2202
|
-
* @param {Api} api Api to send the requests through
|
|
2203
|
-
*/
|
|
2204
|
-
constructor(slice, api) {
|
|
2503
|
+
class ApiCall {
|
|
2504
|
+
constructor(api, slice) {
|
|
2205
2505
|
__publicField(this, "operation");
|
|
2206
2506
|
__publicField(this, "popField");
|
|
2207
2507
|
__publicField(this, "request", {});
|
|
2208
2508
|
/** Log response automatically */
|
|
2209
2509
|
__publicField(this, "debugging");
|
|
2210
|
-
/** Unsubscribe from changes, undefined if not subscribed */
|
|
2211
|
-
__publicField(this, "unsubscribe");
|
|
2212
|
-
/** Cached slice data as an observable */
|
|
2213
|
-
__publicField(this, "cache$", new BehaviorSubject([]));
|
|
2214
2510
|
/**
|
|
2215
2511
|
* Whitelist and alias fields. Alias of `fields()`
|
|
2216
2512
|
* @example
|
|
@@ -2222,16 +2518,8 @@ const _Slice = class _Slice {
|
|
|
2222
2518
|
* @return {Slice<T>}
|
|
2223
2519
|
*/
|
|
2224
2520
|
__publicField(this, "alias", this.fields);
|
|
2225
|
-
this.slice = slice;
|
|
2226
2521
|
this.api = api;
|
|
2227
|
-
|
|
2228
|
-
/** Cached slice data */
|
|
2229
|
-
get cache() {
|
|
2230
|
-
return this.cache$.getValue();
|
|
2231
|
-
}
|
|
2232
|
-
/** Set cached data & alert subscribers */
|
|
2233
|
-
set cache(cache) {
|
|
2234
|
-
this.cache$.next(cache);
|
|
2522
|
+
this.slice = slice;
|
|
2235
2523
|
}
|
|
2236
2524
|
/** Get raw API request */
|
|
2237
2525
|
get raw() {
|
|
@@ -2326,7 +2614,8 @@ const _Slice = class _Slice {
|
|
|
2326
2614
|
*/
|
|
2327
2615
|
exec(options) {
|
|
2328
2616
|
if (!this.operation) throw new Error("No operation chosen");
|
|
2329
|
-
|
|
2617
|
+
const request = this.raw;
|
|
2618
|
+
return this.api.request(request, options).then((resp) => {
|
|
2330
2619
|
if (this.debugging) console.log(resp);
|
|
2331
2620
|
return resp;
|
|
2332
2621
|
});
|
|
@@ -2443,9 +2732,9 @@ const _Slice = class _Slice {
|
|
|
2443
2732
|
* @param {T} rows Rows to add to slice
|
|
2444
2733
|
* @returns {this<T>}
|
|
2445
2734
|
*/
|
|
2446
|
-
save(
|
|
2735
|
+
save(rows) {
|
|
2447
2736
|
this.operation = "$/slice/multisave";
|
|
2448
|
-
this.request.rows = rows;
|
|
2737
|
+
this.request.rows = makeArray(rows);
|
|
2449
2738
|
return this;
|
|
2450
2739
|
}
|
|
2451
2740
|
/**
|
|
@@ -2466,30 +2755,6 @@ const _Slice = class _Slice {
|
|
|
2466
2755
|
}
|
|
2467
2756
|
return this;
|
|
2468
2757
|
}
|
|
2469
|
-
/**
|
|
2470
|
-
* Synchronize cache with server
|
|
2471
|
-
* @example
|
|
2472
|
-
* ```ts
|
|
2473
|
-
* const slice: Slice = new Slice<T>(Slices.Contact);
|
|
2474
|
-
* slice.sync().subscribe((rows: T[]) => {});
|
|
2475
|
-
* ```
|
|
2476
|
-
* @param {boolean} on Enable/disable events
|
|
2477
|
-
* @return {BehaviorSubject<T[]>} Cache which can be subscribed to
|
|
2478
|
-
*/
|
|
2479
|
-
sync(on = true) {
|
|
2480
|
-
if (on) {
|
|
2481
|
-
new _Slice(this.slice, this.api).select().rows().exec().then((rows) => this.cache = rows);
|
|
2482
|
-
if (!this.unsubscribe) this.unsubscribe = this.api.socket.sliceEvents(this.slice, (event) => {
|
|
2483
|
-
const ids = [...event.data.new, ...event.data.changed];
|
|
2484
|
-
new _Slice(this.slice, this.api).select(ids).rows().exec().then((rows) => this.cache = [...this.cache.filter((c) => c.id != null && !ids.includes(c.id)), ...rows]);
|
|
2485
|
-
this.cache = this.cache.filter((v) => v.id && !event.data.lost.includes(v.id));
|
|
2486
|
-
});
|
|
2487
|
-
return this.cache$;
|
|
2488
|
-
} else if (this.unsubscribe) {
|
|
2489
|
-
this.unsubscribe();
|
|
2490
|
-
this.unsubscribe = null;
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
2758
|
/**
|
|
2494
2759
|
* Set the request type to update
|
|
2495
2760
|
* @example
|
|
@@ -2532,6 +2797,7 @@ const _Slice = class _Slice {
|
|
|
2532
2797
|
Object.entries(field).forEach(([key, value2]) => this.where(key, "==", value2));
|
|
2533
2798
|
} else {
|
|
2534
2799
|
const w = raw ? field : [(() => {
|
|
2800
|
+
if (operator == null ? void 0 : operator.startsWith("$")) return operator;
|
|
2535
2801
|
if (operator == "==") return "$eq";
|
|
2536
2802
|
if (operator == "!=") return "$neq";
|
|
2537
2803
|
if (operator == ">") return "$gt";
|
|
@@ -2557,9 +2823,270 @@ const _Slice = class _Slice {
|
|
|
2557
2823
|
}
|
|
2558
2824
|
return this;
|
|
2559
2825
|
}
|
|
2560
|
-
}
|
|
2561
|
-
|
|
2562
|
-
|
|
2826
|
+
}
|
|
2827
|
+
class Slice {
|
|
2828
|
+
/**
|
|
2829
|
+
* An object to aid in constructing requests
|
|
2830
|
+
*
|
|
2831
|
+
* @param {number} slice Slice ID to interact with
|
|
2832
|
+
* @param {Api} api Api to send the requests through
|
|
2833
|
+
*/
|
|
2834
|
+
constructor(slice, api) {
|
|
2835
|
+
__publicField(this, "table");
|
|
2836
|
+
__publicField(this, "info");
|
|
2837
|
+
__publicField(this, "pendingInsert", 0);
|
|
2838
|
+
/** Unsubscribe from changes, undefined if not subscribed */
|
|
2839
|
+
__publicField(this, "unsubscribe");
|
|
2840
|
+
/** Cached slice data as an observable */
|
|
2841
|
+
__publicField(this, "cache$", new BehaviorSubject([]));
|
|
2842
|
+
var _a;
|
|
2843
|
+
this.slice = slice;
|
|
2844
|
+
this.api = api;
|
|
2845
|
+
if (this.offlineEnabled) {
|
|
2846
|
+
this.table = (_a = api.database) == null ? void 0 : _a.table(slice.toString());
|
|
2847
|
+
this.table.getAll().then((resp) => this.cache = resp);
|
|
2848
|
+
this.cache$.pipe(skip(1)).subscribe(async (cache) => {
|
|
2849
|
+
var _a2;
|
|
2850
|
+
await ((_a2 = this.table) == null ? void 0 : _a2.clear());
|
|
2851
|
+
await Promise.all(cache.map((c) => {
|
|
2852
|
+
var _a3;
|
|
2853
|
+
return (_a3 = this.table) == null ? void 0 : _a3.put(c.id, c);
|
|
2854
|
+
}));
|
|
2855
|
+
this.fixIncrement();
|
|
2856
|
+
});
|
|
2857
|
+
this.sync();
|
|
2858
|
+
window.addEventListener("online", async () => {
|
|
2859
|
+
if (this.api.expired) await lastValueFrom(this.api.auth.user$.pipe(skip(1), takeWhile((u) => !u || this.api.expired, true)));
|
|
2860
|
+
this.pushChanges();
|
|
2861
|
+
});
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
/** Cached slice data */
|
|
2865
|
+
get cache() {
|
|
2866
|
+
return this.cache$.getValue();
|
|
2867
|
+
}
|
|
2868
|
+
/** Set cached data & alert subscribers */
|
|
2869
|
+
set cache(cache) {
|
|
2870
|
+
this.cache$.next(cache);
|
|
2871
|
+
}
|
|
2872
|
+
/** Is slice offline support enabled */
|
|
2873
|
+
get offlineEnabled() {
|
|
2874
|
+
var _a;
|
|
2875
|
+
return (_a = this.api.database) == null ? void 0 : _a.includes(this.slice.toString());
|
|
2876
|
+
}
|
|
2877
|
+
fixIncrement() {
|
|
2878
|
+
var _a;
|
|
2879
|
+
this.pendingInsert = ((_a = this.cache.toSorted(sortByProp("id")).pop()) == null ? void 0 : _a["id"]) || 0;
|
|
2880
|
+
}
|
|
2881
|
+
execWrapper(call) {
|
|
2882
|
+
const onlineExec = call.exec.bind(call);
|
|
2883
|
+
return () => {
|
|
2884
|
+
if (this.offlineEnabled && navigator && !(navigator == null ? void 0 : navigator.onLine)) {
|
|
2885
|
+
const where = (row, condition) => {
|
|
2886
|
+
if (Array.isArray(condition) ? !condition.length : condition == null) return true;
|
|
2887
|
+
if (!Array.isArray(condition)) return condition;
|
|
2888
|
+
if (condition[0] == "$field") return row[condition[1]];
|
|
2889
|
+
if (condition[0] == "$not") return !where(row, condition.slice(1));
|
|
2890
|
+
if (condition[0] == "$and") return condition.slice(1).filter((v) => where(row, v)).length == condition.length - 1;
|
|
2891
|
+
if (condition[0] == "$or") return !!condition.slice(1).find((v) => where(row, v));
|
|
2892
|
+
if (condition[0] == "$eq") return where(row, condition[1]) == where(row, condition[2]);
|
|
2893
|
+
if (condition[0] == "$neq") return where(row, condition[1]) != where(row, condition[2]);
|
|
2894
|
+
if (condition[0] == "$gt") return where(row, condition[1]) > where(row, condition[2]);
|
|
2895
|
+
if (condition[0] == "$gte") return where(row, condition[1]) >= where(row, condition[2]);
|
|
2896
|
+
if (condition[0] == "$lt") return where(row, condition[1]) < where(row, condition[2]);
|
|
2897
|
+
if (condition[0] == "$lte") return where(row, condition[1]) <= where(row, condition[2]);
|
|
2898
|
+
if (condition[0] == "$mod") return where(row, condition[1]) % where(row, condition[2]);
|
|
2899
|
+
return condition[0];
|
|
2900
|
+
};
|
|
2901
|
+
let request = call.raw, resp = {};
|
|
2902
|
+
if (request["$/slice/delete"]) {
|
|
2903
|
+
const found = this.cache.filter((r) => where(r, request["$/slice/delete"]["where"])).map((r) => r.id);
|
|
2904
|
+
this.cache = this.cache.map((r) => found.includes(r.id) ? { ...r, _sync: "delete" } : r);
|
|
2905
|
+
resp = {
|
|
2906
|
+
affected: found.length,
|
|
2907
|
+
"ignored-keys": [],
|
|
2908
|
+
keys: found,
|
|
2909
|
+
tx: null
|
|
2910
|
+
};
|
|
2911
|
+
} else if (request["$/slice/report"]) {
|
|
2912
|
+
resp["rows"] = deepCopy(this.cache).filter((r) => (r == null ? void 0 : r._sync) != "delete").filter((r) => r && where(r, request["$/slice/report"]["where"]));
|
|
2913
|
+
if (request["order"]) resp["rows"] = resp["rows"].toSorted(sortByProp(request["order"][1][1], request["order"] == "$desc"));
|
|
2914
|
+
if (request["limit"]) resp["rows"] = resp["rows"].slice(0, request["limit"]);
|
|
2915
|
+
if (request["fields"]) {
|
|
2916
|
+
if (request["fields"]["$count"]) resp["rows"] = { count: resp["rows"].length };
|
|
2917
|
+
else resp["rows"] = resp["rows"].map((r) => Object.entries(request["fields"]).reduce((acc, [k, v]) => ({
|
|
2918
|
+
...acc,
|
|
2919
|
+
[v]: r[k]
|
|
2920
|
+
}), {}));
|
|
2921
|
+
}
|
|
2922
|
+
} else if (request["$/slice/xinsert"]) {
|
|
2923
|
+
const rows = request["$/slice/xinsert"]["rows"].map((r) => ({
|
|
2924
|
+
...r,
|
|
2925
|
+
id: -++this.pendingInsert,
|
|
2926
|
+
_sync: "insert"
|
|
2927
|
+
}));
|
|
2928
|
+
this.cache = [...this.cache, ...rows];
|
|
2929
|
+
resp = {
|
|
2930
|
+
failed: [],
|
|
2931
|
+
granted: rows.map((r) => Object.keys(r).reduce((acc, key) => ({ ...acc, [key]: true }), {})),
|
|
2932
|
+
"ignored-fields": [],
|
|
2933
|
+
keys: rows.map((r) => r.id),
|
|
2934
|
+
publish: 1,
|
|
2935
|
+
tx: null
|
|
2936
|
+
};
|
|
2937
|
+
} else if (request["$/slice/xupdate"]) {
|
|
2938
|
+
const ids = request["$/slice/xupdate"]["rows"].map((r) => r.id);
|
|
2939
|
+
this.cache = [
|
|
2940
|
+
...this.cache.filter((c) => !ids.includes(c.id)),
|
|
2941
|
+
...request["$/slice/xupdate"]["rows"].map((r) => ({ ...r, _sync: "update" }))
|
|
2942
|
+
].toSorted(sortByProp("id"));
|
|
2943
|
+
resp = {
|
|
2944
|
+
failed: [],
|
|
2945
|
+
granted: request["$/slice/xupdate"]["rows"].map((r) => Object.keys(r).reduce((acc, key) => ({
|
|
2946
|
+
...acc,
|
|
2947
|
+
[key]: true
|
|
2948
|
+
}), {})),
|
|
2949
|
+
"ignored-fields": [],
|
|
2950
|
+
keys: request["$/slice/xupdate"]["rows"].map((r) => r.id),
|
|
2951
|
+
publish: 1,
|
|
2952
|
+
tx: null
|
|
2953
|
+
};
|
|
2954
|
+
}
|
|
2955
|
+
if (request["$pop"]) {
|
|
2956
|
+
resp = request["$pop"].split(":").reduce((acc, key) => acc[JSONAttemptParse(key)], resp);
|
|
2957
|
+
}
|
|
2958
|
+
return Promise.resolve(resp);
|
|
2959
|
+
} else {
|
|
2960
|
+
return onlineExec();
|
|
2961
|
+
}
|
|
2962
|
+
};
|
|
2963
|
+
}
|
|
2964
|
+
async pushChanges() {
|
|
2965
|
+
if (this.offlineEnabled && navigator && navigator.onLine) {
|
|
2966
|
+
await Promise.allSettled(
|
|
2967
|
+
this.cache.values().map((value) => {
|
|
2968
|
+
if (value._sync == "delete") {
|
|
2969
|
+
return this.delete(value.id).exec();
|
|
2970
|
+
} else if (value._sync == "insert") {
|
|
2971
|
+
return this.insert({ ...value, id: void 0, _sync: void 0 }).exec();
|
|
2972
|
+
} else if (value._sync == "update") {
|
|
2973
|
+
return this.update({
|
|
2974
|
+
...value,
|
|
2975
|
+
_sync: void 0
|
|
2976
|
+
}).where("_updatedDate", "==", value.modified).exec();
|
|
2977
|
+
}
|
|
2978
|
+
}).filter((r) => !!r)
|
|
2979
|
+
);
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
/**
|
|
2983
|
+
* Get slice information
|
|
2984
|
+
* @param reload Ignore cache & reload info
|
|
2985
|
+
* @return {Promise<SliceInfo>}
|
|
2986
|
+
*/
|
|
2987
|
+
async getInfo(reload) {
|
|
2988
|
+
var _a, _b, _c;
|
|
2989
|
+
const getType = (field) => {
|
|
2990
|
+
if (field.options) {
|
|
2991
|
+
let t = field.options.split("|").map((o) => `'${o}'`).join(" | ");
|
|
2992
|
+
if (field.type == "control_checkbox") t = `(${t})[]`;
|
|
2993
|
+
return t;
|
|
2994
|
+
}
|
|
2995
|
+
if (field.type == "control_checkbox" || field.type == "boolean") return "boolean";
|
|
2996
|
+
if (field.type == "control_new_date" || field.type == "control_new_time" || field.type == "control_new_datetime" || field.type == "Date")
|
|
2997
|
+
return "Date";
|
|
2998
|
+
if (field.type == "control_number" || field.type == "number") return "number";
|
|
2999
|
+
return "string";
|
|
3000
|
+
};
|
|
3001
|
+
if (this.info && !reload) return Promise.resolve(this.info);
|
|
3002
|
+
this.info = await this.api.request({ "$/slice/fetch": { slice: this.slice } });
|
|
3003
|
+
const fields = ((_c = (_b = (_a = this.info) == null ? void 0 : _a.meta) == null ? void 0 : _b.presentation) == null ? void 0 : _c.fields) ? Object.values(this.info.meta.presentation.fields) : [];
|
|
3004
|
+
this.info.types = fields.filter((value) => value.id != void 0).map((value) => {
|
|
3005
|
+
var _a2;
|
|
3006
|
+
return {
|
|
3007
|
+
key: value.id.toString(),
|
|
3008
|
+
options: (_a2 = value.options) == null ? void 0 : _a2.split("|"),
|
|
3009
|
+
readonly: !!value.readonly || value.id.startsWith("fid") || ["id", "creatorRef", "created", "modifierRef", "modified"].includes(value.id),
|
|
3010
|
+
required: !!value.required,
|
|
3011
|
+
type: getType(value)
|
|
3012
|
+
};
|
|
3013
|
+
});
|
|
3014
|
+
return this.info;
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* Synchronize cache with server
|
|
3018
|
+
* @example
|
|
3019
|
+
* ```ts
|
|
3020
|
+
* const slice: Slice = new Slice<T>(Slices.Contact);
|
|
3021
|
+
* slice.sync().subscribe((rows: T[]) => {});
|
|
3022
|
+
* ```
|
|
3023
|
+
* @param {boolean} on Enable/disable events
|
|
3024
|
+
* @return {BehaviorSubject<T[]>} Cache which can be subscribed to
|
|
3025
|
+
*/
|
|
3026
|
+
sync(on = true) {
|
|
3027
|
+
if (on) {
|
|
3028
|
+
this.pushChanges().then(() => this.select().rows().exec().then((rows) => this.cache = rows));
|
|
3029
|
+
if (!this.unsubscribe) this.unsubscribe = this.api.socket.sliceEvents(this.slice, (event) => {
|
|
3030
|
+
const ids = [...event.data.new, ...event.data.changed];
|
|
3031
|
+
this.select(ids).rows().exec().then((rows) => this.cache = [...this.cache.filter((c) => c.id != null && !ids.includes(c.id)), ...rows]);
|
|
3032
|
+
this.cache = this.cache.filter((v) => v.id && !event.data.lost.includes(v.id));
|
|
3033
|
+
});
|
|
3034
|
+
return this.cache$;
|
|
3035
|
+
} else if (this.unsubscribe) {
|
|
3036
|
+
this.unsubscribe();
|
|
3037
|
+
this.unsubscribe = null;
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
// Transaction wrapper =============================================================================================
|
|
3041
|
+
/**
|
|
3042
|
+
* {@inheritDoc ApiCall.count}
|
|
3043
|
+
*/
|
|
3044
|
+
count(arg = "id") {
|
|
3045
|
+
const call = new ApiCall(this.api, this.slice);
|
|
3046
|
+
call.exec = this.execWrapper(call);
|
|
3047
|
+
return call.count(arg);
|
|
3048
|
+
}
|
|
3049
|
+
/**
|
|
3050
|
+
* {@inheritDoc ApiCall.delete}
|
|
3051
|
+
*/
|
|
3052
|
+
delete(id) {
|
|
3053
|
+
const call = new ApiCall(this.api, this.slice);
|
|
3054
|
+
call.exec = this.execWrapper(call);
|
|
3055
|
+
return call.delete(id);
|
|
3056
|
+
}
|
|
3057
|
+
/**
|
|
3058
|
+
* {@inheritDoc ApiCall.insert}
|
|
3059
|
+
*/
|
|
3060
|
+
insert(rows) {
|
|
3061
|
+
const call = new ApiCall(this.api, this.slice);
|
|
3062
|
+
call.exec = this.execWrapper(call);
|
|
3063
|
+
return call.insert(rows);
|
|
3064
|
+
}
|
|
3065
|
+
/**
|
|
3066
|
+
* {@inheritDoc ApiCall.save}
|
|
3067
|
+
*/
|
|
3068
|
+
save(rows) {
|
|
3069
|
+
const call = new ApiCall(this.api, this.slice);
|
|
3070
|
+
call.exec = this.execWrapper(call);
|
|
3071
|
+
return call.save(rows);
|
|
3072
|
+
}
|
|
3073
|
+
/**
|
|
3074
|
+
* {@inheritDoc ApiCall.select}
|
|
3075
|
+
*/
|
|
3076
|
+
select(id) {
|
|
3077
|
+
const call = new ApiCall(this.api, this.slice);
|
|
3078
|
+
call.exec = this.execWrapper(call);
|
|
3079
|
+
return call.select(id);
|
|
3080
|
+
}
|
|
3081
|
+
/**
|
|
3082
|
+
* {@inheritDoc ApiCall.update}
|
|
3083
|
+
*/
|
|
3084
|
+
update(rows) {
|
|
3085
|
+
const call = new ApiCall(this.api, this.slice);
|
|
3086
|
+
call.exec = this.execWrapper(call);
|
|
3087
|
+
return call.update(rows);
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
2563
3090
|
class Socket {
|
|
2564
3091
|
constructor(api, options = {}) {
|
|
2565
3092
|
__publicField(this, "listeners", []);
|
|
@@ -2692,8 +3219,8 @@ class Superuser {
|
|
|
2692
3219
|
} });
|
|
2693
3220
|
}
|
|
2694
3221
|
}
|
|
2695
|
-
const version = "1.0
|
|
2696
|
-
class
|
|
3222
|
+
const version = "1.1.0-rc1";
|
|
3223
|
+
const _Api = class _Api {
|
|
2697
3224
|
/**
|
|
2698
3225
|
* Connect to Datalynk & send requests
|
|
2699
3226
|
*
|
|
@@ -2702,10 +3229,10 @@ class Api {
|
|
|
2702
3229
|
* const api = new Api('https://spoke.auxiliumgroup.com');
|
|
2703
3230
|
* ```
|
|
2704
3231
|
*
|
|
2705
|
-
* @param {string}
|
|
3232
|
+
* @param {string} origin API URL
|
|
2706
3233
|
* @param {ApiOptions} options
|
|
2707
3234
|
*/
|
|
2708
|
-
constructor(
|
|
3235
|
+
constructor(origin, options = {}) {
|
|
2709
3236
|
/** Current requests bundle */
|
|
2710
3237
|
__publicField(this, "bundle", []);
|
|
2711
3238
|
/** Bundle lifecycle tracking */
|
|
@@ -2714,12 +3241,6 @@ class Api {
|
|
|
2714
3241
|
__publicField(this, "localStorageKey", "datalynk-token");
|
|
2715
3242
|
/** Pending requests cache */
|
|
2716
3243
|
__publicField(this, "pending", {});
|
|
2717
|
-
/** API URL */
|
|
2718
|
-
__publicField(this, "url");
|
|
2719
|
-
/** Package version */
|
|
2720
|
-
__publicField(this, "version", version);
|
|
2721
|
-
/** API Session token */
|
|
2722
|
-
__publicField(this, "token$", new BehaviorSubject(void 0));
|
|
2723
3244
|
/** Helpers */
|
|
2724
3245
|
/** Authentication */
|
|
2725
3246
|
__publicField(this, "auth");
|
|
@@ -2731,12 +3252,26 @@ class Api {
|
|
|
2731
3252
|
__publicField(this, "socket");
|
|
2732
3253
|
/** Superuser */
|
|
2733
3254
|
__publicField(this, "superuser");
|
|
2734
|
-
|
|
2735
|
-
this
|
|
3255
|
+
/** Offline database */
|
|
3256
|
+
__publicField(this, "database");
|
|
3257
|
+
/** Options */
|
|
3258
|
+
__publicField(this, "options");
|
|
3259
|
+
/** Created slices */
|
|
3260
|
+
__publicField(this, "sliceCache", /* @__PURE__ */ new Map());
|
|
3261
|
+
/** API URL */
|
|
3262
|
+
__publicField(this, "url");
|
|
3263
|
+
/** Client library version */
|
|
3264
|
+
__publicField(this, "version", version);
|
|
3265
|
+
/** API Session token */
|
|
3266
|
+
__publicField(this, "token$", new BehaviorSubject(void 0));
|
|
3267
|
+
var _a, _b;
|
|
3268
|
+
this.origin = origin;
|
|
3269
|
+
this.url = `${new URL(origin).origin}/api/`;
|
|
2736
3270
|
this.options = {
|
|
2737
|
-
|
|
3271
|
+
offline: [],
|
|
2738
3272
|
origin: typeof location !== "undefined" ? location.host : "Unknown",
|
|
2739
3273
|
saveSession: true,
|
|
3274
|
+
serviceWorker: "/service.worker.js",
|
|
2740
3275
|
...options
|
|
2741
3276
|
};
|
|
2742
3277
|
if (this.options.saveSession) {
|
|
@@ -2747,12 +3282,32 @@ class Api {
|
|
|
2747
3282
|
else localStorage.removeItem(this.localStorageKey);
|
|
2748
3283
|
});
|
|
2749
3284
|
}
|
|
2750
|
-
|
|
3285
|
+
this.socket = new Socket(this, { url: options.socket });
|
|
2751
3286
|
this.auth = new Auth(this);
|
|
2752
3287
|
this.files = new Files(this);
|
|
2753
3288
|
this.pdf = new Pdf(this);
|
|
2754
3289
|
this.superuser = new Superuser(this);
|
|
2755
|
-
this.
|
|
3290
|
+
if ((_a = this.options.offline) == null ? void 0 : _a.length) {
|
|
3291
|
+
if (typeof indexedDB == "undefined") throw new Error("Cannot enable offline support, indexedDB is not available in this environment");
|
|
3292
|
+
this.database = new Database("datalynk", this.options.offline);
|
|
3293
|
+
(_b = this.options.offline) == null ? void 0 : _b.forEach((id) => this.slice(id));
|
|
3294
|
+
this.cacheUrl();
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
/** Get session info from JWT payload */
|
|
3298
|
+
get jwtPayload() {
|
|
3299
|
+
if (!this.token) return null;
|
|
3300
|
+
return decodeJwt(this.token);
|
|
3301
|
+
}
|
|
3302
|
+
/** Is token expired */
|
|
3303
|
+
get expired() {
|
|
3304
|
+
var _a;
|
|
3305
|
+
return (((_a = this.jwtPayload) == null ? void 0 : _a.exp) ?? Infinity) * 1e3 <= Date.now();
|
|
3306
|
+
}
|
|
3307
|
+
/** Logged in spoke */
|
|
3308
|
+
get spoke() {
|
|
3309
|
+
var _a;
|
|
3310
|
+
return (_a = this.jwtPayload) == null ? void 0 : _a.realm;
|
|
2756
3311
|
}
|
|
2757
3312
|
get token() {
|
|
2758
3313
|
return this.token$.getValue();
|
|
@@ -2760,11 +3315,6 @@ class Api {
|
|
|
2760
3315
|
set token(token) {
|
|
2761
3316
|
this.token$.next(token);
|
|
2762
3317
|
}
|
|
2763
|
-
/** Get session info from JWT payload */
|
|
2764
|
-
get jwtPayload() {
|
|
2765
|
-
if (!this.token) return null;
|
|
2766
|
-
return decodeJwt(this.token);
|
|
2767
|
-
}
|
|
2768
3318
|
_request(req, options = {}) {
|
|
2769
3319
|
const token = options.token || this.token;
|
|
2770
3320
|
return fetch(this.url, {
|
|
@@ -2774,14 +3324,31 @@ class Api {
|
|
|
2774
3324
|
"Content-Type": "application/json",
|
|
2775
3325
|
"X-Date-Return-Format": this.options.legacyDates ? void 0 : "ISO8601"
|
|
2776
3326
|
}),
|
|
2777
|
-
body: JSON.stringify(
|
|
3327
|
+
body: JSON.stringify(_Api.translateTokens(req))
|
|
2778
3328
|
}).then(async (resp) => {
|
|
2779
3329
|
let data = JSONAttemptParse(await resp.text());
|
|
2780
3330
|
if (!resp.ok || (data == null ? void 0 : data.error)) throw Object.assign(errorFromCode(resp.status, data.error), data);
|
|
2781
|
-
if (!options.raw) data =
|
|
3331
|
+
if (!options.raw) data = _Api.translateTokens(data);
|
|
2782
3332
|
return data;
|
|
2783
3333
|
});
|
|
2784
3334
|
}
|
|
3335
|
+
async cacheUrl() {
|
|
3336
|
+
if (!this.options.serviceWorker || !("serviceWorker" in navigator)) return;
|
|
3337
|
+
await navigator.serviceWorker.getRegistration(this.options.serviceWorker).then((reg) => reg ?? navigator.serviceWorker.register(this.options.serviceWorker, { scope: "/" }));
|
|
3338
|
+
await navigator.serviceWorker.ready;
|
|
3339
|
+
if (!navigator.serviceWorker.controller) return window.location.reload();
|
|
3340
|
+
}
|
|
3341
|
+
/**
|
|
3342
|
+
* Get list of slices
|
|
3343
|
+
* @return {Promise<number[]>}
|
|
3344
|
+
*/
|
|
3345
|
+
getSlices() {
|
|
3346
|
+
return this.request({ "$/tools/action_chain": [
|
|
3347
|
+
{ "!/env/me": {} },
|
|
3348
|
+
{ "!/slice/permissionsLite": {} },
|
|
3349
|
+
{ "!/tools/column": { "col": "id", "rows": { "$_": "1:rows" } } }
|
|
3350
|
+
] });
|
|
3351
|
+
}
|
|
2785
3352
|
/**
|
|
2786
3353
|
* Parses API request/response object for special Datalynk tokens & converts them to native JS objects
|
|
2787
3354
|
*
|
|
@@ -2800,6 +3367,10 @@ class Api {
|
|
|
2800
3367
|
val = Serializer.deserialize[index](val[index]);
|
|
2801
3368
|
} else if (index in Serializer.serialize) {
|
|
2802
3369
|
val = Serializer.serialize[index](val[index]);
|
|
3370
|
+
} else if (val[index] == "Yes") {
|
|
3371
|
+
val[index] = true;
|
|
3372
|
+
} else if (val[index] == "No") {
|
|
3373
|
+
val[index] = false;
|
|
2803
3374
|
} else {
|
|
2804
3375
|
queue.push(`${key}.${index}`);
|
|
2805
3376
|
}
|
|
@@ -2816,7 +3387,7 @@ class Api {
|
|
|
2816
3387
|
chain(...requests) {
|
|
2817
3388
|
const arr = requests.length == 1 && Array.isArray(requests[0]) ? requests[0] : requests;
|
|
2818
3389
|
return this.request({ "$/tools/action_chain": arr.map((r) => {
|
|
2819
|
-
if (!(r instanceof
|
|
3390
|
+
if (!(r instanceof ApiCall)) return r;
|
|
2820
3391
|
const req = r.raw;
|
|
2821
3392
|
Object.keys(req).forEach((key) => {
|
|
2822
3393
|
if (key.startsWith("$/")) {
|
|
@@ -2835,7 +3406,7 @@ class Api {
|
|
|
2835
3406
|
chainMap(request) {
|
|
2836
3407
|
return this.request({ "$/tools/do": [
|
|
2837
3408
|
...Object.entries(request).flatMap(([key, r]) => {
|
|
2838
|
-
if (!(r instanceof
|
|
3409
|
+
if (!(r instanceof ApiCall)) return [key, r];
|
|
2839
3410
|
const req = r.raw;
|
|
2840
3411
|
Object.keys(req).forEach((key2) => {
|
|
2841
3412
|
if (key2.startsWith("$/")) {
|
|
@@ -2916,15 +3487,20 @@ class Api {
|
|
|
2916
3487
|
* const unsubscribe = contactsSlice.sync().subscribe(rows => console.log(rows));
|
|
2917
3488
|
* ```
|
|
2918
3489
|
*
|
|
2919
|
-
* @param {number}
|
|
3490
|
+
* @param {number} id Slice ID the object will target
|
|
2920
3491
|
* @returns {Slice<T = any>} Object for making requests & caching rows
|
|
2921
3492
|
*/
|
|
2922
|
-
slice(
|
|
2923
|
-
|
|
3493
|
+
slice(id) {
|
|
3494
|
+
if (!this.sliceCache.has(+id)) this.sliceCache.set(+id, new Slice(id, this));
|
|
3495
|
+
return this.sliceCache.get(+id);
|
|
2924
3496
|
}
|
|
2925
|
-
}
|
|
3497
|
+
};
|
|
3498
|
+
/** Client library version */
|
|
3499
|
+
__publicField(_Api, "version", version);
|
|
3500
|
+
let Api = _Api;
|
|
2926
3501
|
export {
|
|
2927
3502
|
Api,
|
|
3503
|
+
ApiCall,
|
|
2928
3504
|
Auth,
|
|
2929
3505
|
Files,
|
|
2930
3506
|
LoginPrompt,
|
|
@@ -2932,5 +3508,6 @@ export {
|
|
|
2932
3508
|
Serializer,
|
|
2933
3509
|
Slice,
|
|
2934
3510
|
Socket,
|
|
2935
|
-
Superuser
|
|
3511
|
+
Superuser,
|
|
3512
|
+
getTheme
|
|
2936
3513
|
};
|