@auxilium/datalynk-client 1.0.20 → 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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3513 -0
- package/dist/index.mjs +681 -103
- 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 -2939
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,9 +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
|
+
}
|
|
35
186
|
function makeArray(value) {
|
|
36
187
|
return Array.isArray(value) ? value : [value];
|
|
37
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
|
+
}
|
|
38
268
|
function contrast(background) {
|
|
39
269
|
const exploded = background == null ? void 0 : background.match(background.length >= 6 ? /[0-9a-fA-F]{2}/g : /[0-9a-fA-F]/g);
|
|
40
270
|
if (!exploded || (exploded == null ? void 0 : exploded.length) < 3) return "black";
|
|
@@ -1486,6 +1716,33 @@ var BehaviorSubject = function(_super) {
|
|
|
1486
1716
|
};
|
|
1487
1717
|
return BehaviorSubject2;
|
|
1488
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
|
+
}
|
|
1489
1746
|
function filter(predicate, thisArg) {
|
|
1490
1747
|
return operate(function(source, subscriber) {
|
|
1491
1748
|
var index = 0;
|
|
@@ -1515,6 +1772,34 @@ function distinctUntilChanged(comparator, keySelector) {
|
|
|
1515
1772
|
function defaultCompare(a, b) {
|
|
1516
1773
|
return a === b;
|
|
1517
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
|
+
}
|
|
1518
1803
|
const _LoginPrompt = class _LoginPrompt {
|
|
1519
1804
|
constructor(api, spoke, options = {}) {
|
|
1520
1805
|
__publicField(this, "alert");
|
|
@@ -1523,7 +1808,6 @@ const _LoginPrompt = class _LoginPrompt {
|
|
|
1523
1808
|
__publicField(this, "password");
|
|
1524
1809
|
__publicField(this, "persist");
|
|
1525
1810
|
__publicField(this, "username");
|
|
1526
|
-
__publicField(this, "options");
|
|
1527
1811
|
__publicField(this, "_done");
|
|
1528
1812
|
/** Promise which resolves once login is complete */
|
|
1529
1813
|
__publicField(this, "done", new Promise((res) => {
|
|
@@ -1531,13 +1815,10 @@ const _LoginPrompt = class _LoginPrompt {
|
|
|
1531
1815
|
}));
|
|
1532
1816
|
this.api = api;
|
|
1533
1817
|
this.spoke = spoke;
|
|
1534
|
-
this.options =
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
textColor: "#000000",
|
|
1539
|
-
...clean(options, true)
|
|
1540
|
-
};
|
|
1818
|
+
this.options = options;
|
|
1819
|
+
this.themeDefaults().then(() => this.render());
|
|
1820
|
+
}
|
|
1821
|
+
async render() {
|
|
1541
1822
|
this.close();
|
|
1542
1823
|
document.head.innerHTML += _LoginPrompt.css(this.options);
|
|
1543
1824
|
const div = document.createElement("div");
|
|
@@ -1549,9 +1830,17 @@ const _LoginPrompt = class _LoginPrompt {
|
|
|
1549
1830
|
this.password = document.querySelector('#datalynk-login-form input[name="password"]');
|
|
1550
1831
|
this.persist = document.querySelector('#datalynk-login-form input[name="persist"]');
|
|
1551
1832
|
this.username = document.querySelector('#datalynk-login-form input[name="username"]');
|
|
1552
|
-
if (options.persist === false) this.persist.parentElement.remove();
|
|
1833
|
+
if (this.options.persist === false) this.persist.parentElement.remove();
|
|
1553
1834
|
this.form.onsubmit = (event) => this.login(event);
|
|
1554
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
|
+
}
|
|
1555
1844
|
/** Close the login prompt */
|
|
1556
1845
|
close() {
|
|
1557
1846
|
var _a, _b;
|
|
@@ -1597,11 +1886,11 @@ __publicField(_LoginPrompt, "css", (options) => `
|
|
|
1597
1886
|
@import url('https://fonts.cdnfonts.com/css/ar-blanca');
|
|
1598
1887
|
|
|
1599
1888
|
#datalynk-login {
|
|
1600
|
-
--theme-background: ${options.background};
|
|
1889
|
+
--theme-background: ${options.backgroundColor ? options.backgroundColor : `url(${options.background})`};
|
|
1601
1890
|
--theme-container: #000000cc;
|
|
1602
1891
|
--theme-glow: ${options.glow || options.color};
|
|
1603
1892
|
--theme-primary: ${options.color};
|
|
1604
|
-
--theme-text: ${options.textColor}
|
|
1893
|
+
--theme-text: ${options.textColor};
|
|
1605
1894
|
|
|
1606
1895
|
position: fixed;
|
|
1607
1896
|
left: 0;
|
|
@@ -1614,7 +1903,7 @@ __publicField(_LoginPrompt, "css", (options) => `
|
|
|
1614
1903
|
font-family: sans-serif;
|
|
1615
1904
|
z-index: 1000;
|
|
1616
1905
|
}
|
|
1617
|
-
|
|
1906
|
+
|
|
1618
1907
|
#datalynk-login .added-links {
|
|
1619
1908
|
color: var(--theme-text);
|
|
1620
1909
|
position: fixed;
|
|
@@ -1750,12 +2039,18 @@ __publicField(_LoginPrompt, "css", (options) => `
|
|
|
1750
2039
|
/** Dynamically create HTML */
|
|
1751
2040
|
__publicField(_LoginPrompt, "template", (options) => `
|
|
1752
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
|
+
|
|
1753
2047
|
<div class="added-links">
|
|
1754
2048
|
${(options.addLinks || []).map((link) => `<a href="${link.url || "#"}" target="_blank">${link.text}</a>`).join(" | ")}
|
|
1755
2049
|
</div>
|
|
1756
2050
|
<div class="login-container">
|
|
1757
2051
|
<div class="login-header">
|
|
1758
|
-
${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>
|
|
1759
2054
|
</div>
|
|
1760
2055
|
<div class="login-content">
|
|
1761
2056
|
<div class="login-body" style="max-width: 300px">
|
|
@@ -1801,13 +2096,17 @@ __publicField(_LoginPrompt, "template", (options) => `
|
|
|
1801
2096
|
let LoginPrompt = _LoginPrompt;
|
|
1802
2097
|
class Auth {
|
|
1803
2098
|
constructor(api) {
|
|
2099
|
+
__publicField(this, "onlinePrompt");
|
|
1804
2100
|
/** Current user as an observable */
|
|
1805
2101
|
__publicField(this, "user$", new BehaviorSubject(void 0));
|
|
2102
|
+
var _a;
|
|
1806
2103
|
this.api = api;
|
|
1807
2104
|
this.api.token$.subscribe(async (token) => {
|
|
1808
2105
|
if (token === void 0) return;
|
|
1809
2106
|
this.user = await this.current(token);
|
|
1810
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)));
|
|
1811
2110
|
}
|
|
1812
2111
|
/** Current user */
|
|
1813
2112
|
get user() {
|
|
@@ -1830,7 +2129,9 @@ class Auth {
|
|
|
1830
2129
|
var _a;
|
|
1831
2130
|
if (!token) return null;
|
|
1832
2131
|
else if (token == ((_a = this.user) == null ? void 0 : _a.token)) return this.user;
|
|
1833
|
-
|
|
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);
|
|
1834
2135
|
}
|
|
1835
2136
|
/**
|
|
1836
2137
|
* Automatically handle sessions by checking localStorage & URL parameters for a token & prompting
|
|
@@ -1841,17 +2142,20 @@ class Auth {
|
|
|
1841
2142
|
* @return {Promise<void>} Login complete
|
|
1842
2143
|
*/
|
|
1843
2144
|
async handleLogin(spoke, options) {
|
|
1844
|
-
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));
|
|
1845
2148
|
const urlToken = new URLSearchParams(location.search).get("datalynkToken");
|
|
1846
2149
|
if (urlToken) {
|
|
1847
2150
|
this.api.token = urlToken;
|
|
1848
2151
|
location.href = location.href.replace(/datalynkToken=.*?(&|$)/gm, "");
|
|
1849
|
-
} else if (this.api.token) {
|
|
1850
|
-
if (((
|
|
2152
|
+
} else if (this.api.token && !this.api.expired) {
|
|
2153
|
+
if (((_b = this.api.jwtPayload) == null ? void 0 : _b.realm) != spoke) {
|
|
1851
2154
|
this.api.token = null;
|
|
1852
2155
|
location.reload();
|
|
1853
2156
|
}
|
|
1854
2157
|
} else {
|
|
2158
|
+
this.api.token = null;
|
|
1855
2159
|
await this.loginPrompt(spoke, options).done;
|
|
1856
2160
|
location.reload();
|
|
1857
2161
|
}
|
|
@@ -1956,6 +2260,7 @@ class Auth {
|
|
|
1956
2260
|
* @return {Promise<{closed: string, new: string}>}
|
|
1957
2261
|
*/
|
|
1958
2262
|
logout() {
|
|
2263
|
+
localStorage.removeItem("datalynk-user");
|
|
1959
2264
|
return this.api.request({ "$/auth/logout": {} }).then((resp) => {
|
|
1960
2265
|
this.api.token = null;
|
|
1961
2266
|
return resp;
|
|
@@ -1970,11 +2275,13 @@ class Auth {
|
|
|
1970
2275
|
* @return {Promise<any>} New session
|
|
1971
2276
|
*/
|
|
1972
2277
|
reset(login, newPassword, code) {
|
|
1973
|
-
return this.api.request({
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2278
|
+
return this.api.request({
|
|
2279
|
+
"$/auth/mobile/rescue": {
|
|
2280
|
+
user: login,
|
|
2281
|
+
password: newPassword,
|
|
2282
|
+
pin: code
|
|
2283
|
+
}
|
|
2284
|
+
}).then((resp) => {
|
|
1978
2285
|
if (resp.token) this.api.token = resp.token;
|
|
1979
2286
|
return resp;
|
|
1980
2287
|
});
|
|
@@ -2033,7 +2340,7 @@ class Files {
|
|
|
2033
2340
|
})).then(async (files2) => {
|
|
2034
2341
|
if (associate) {
|
|
2035
2342
|
let id = typeof associate.row == "number" ? associate.row : associate.row[associate.pk || "id"];
|
|
2036
|
-
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();
|
|
2037
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);
|
|
2038
2345
|
}
|
|
2039
2346
|
return files2;
|
|
@@ -2193,23 +2500,13 @@ const Serializer = {
|
|
|
2193
2500
|
}
|
|
2194
2501
|
}
|
|
2195
2502
|
};
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
* An object to aid in constructing requests
|
|
2199
|
-
*
|
|
2200
|
-
* @param {number} slice Slice ID to interact with
|
|
2201
|
-
* @param {Api} api Api to send the requests through
|
|
2202
|
-
*/
|
|
2203
|
-
constructor(slice, api) {
|
|
2503
|
+
class ApiCall {
|
|
2504
|
+
constructor(api, slice) {
|
|
2204
2505
|
__publicField(this, "operation");
|
|
2205
2506
|
__publicField(this, "popField");
|
|
2206
2507
|
__publicField(this, "request", {});
|
|
2207
2508
|
/** Log response automatically */
|
|
2208
2509
|
__publicField(this, "debugging");
|
|
2209
|
-
/** Unsubscribe from changes, undefined if not subscribed */
|
|
2210
|
-
__publicField(this, "unsubscribe");
|
|
2211
|
-
/** Cached slice data as an observable */
|
|
2212
|
-
__publicField(this, "cache$", new BehaviorSubject([]));
|
|
2213
2510
|
/**
|
|
2214
2511
|
* Whitelist and alias fields. Alias of `fields()`
|
|
2215
2512
|
* @example
|
|
@@ -2221,16 +2518,8 @@ const _Slice = class _Slice {
|
|
|
2221
2518
|
* @return {Slice<T>}
|
|
2222
2519
|
*/
|
|
2223
2520
|
__publicField(this, "alias", this.fields);
|
|
2224
|
-
this.slice = slice;
|
|
2225
2521
|
this.api = api;
|
|
2226
|
-
|
|
2227
|
-
/** Cached slice data */
|
|
2228
|
-
get cache() {
|
|
2229
|
-
return this.cache$.getValue();
|
|
2230
|
-
}
|
|
2231
|
-
/** Set cached data & alert subscribers */
|
|
2232
|
-
set cache(cache) {
|
|
2233
|
-
this.cache$.next(cache);
|
|
2522
|
+
this.slice = slice;
|
|
2234
2523
|
}
|
|
2235
2524
|
/** Get raw API request */
|
|
2236
2525
|
get raw() {
|
|
@@ -2325,7 +2614,8 @@ const _Slice = class _Slice {
|
|
|
2325
2614
|
*/
|
|
2326
2615
|
exec(options) {
|
|
2327
2616
|
if (!this.operation) throw new Error("No operation chosen");
|
|
2328
|
-
|
|
2617
|
+
const request = this.raw;
|
|
2618
|
+
return this.api.request(request, options).then((resp) => {
|
|
2329
2619
|
if (this.debugging) console.log(resp);
|
|
2330
2620
|
return resp;
|
|
2331
2621
|
});
|
|
@@ -2442,9 +2732,9 @@ const _Slice = class _Slice {
|
|
|
2442
2732
|
* @param {T} rows Rows to add to slice
|
|
2443
2733
|
* @returns {this<T>}
|
|
2444
2734
|
*/
|
|
2445
|
-
save(
|
|
2735
|
+
save(rows) {
|
|
2446
2736
|
this.operation = "$/slice/multisave";
|
|
2447
|
-
this.request.rows = rows;
|
|
2737
|
+
this.request.rows = makeArray(rows);
|
|
2448
2738
|
return this;
|
|
2449
2739
|
}
|
|
2450
2740
|
/**
|
|
@@ -2465,30 +2755,6 @@ const _Slice = class _Slice {
|
|
|
2465
2755
|
}
|
|
2466
2756
|
return this;
|
|
2467
2757
|
}
|
|
2468
|
-
/**
|
|
2469
|
-
* Synchronize cache with server
|
|
2470
|
-
* @example
|
|
2471
|
-
* ```ts
|
|
2472
|
-
* const slice: Slice = new Slice<T>(Slices.Contact);
|
|
2473
|
-
* slice.sync().subscribe((rows: T[]) => {});
|
|
2474
|
-
* ```
|
|
2475
|
-
* @param {boolean} on Enable/disable events
|
|
2476
|
-
* @return {BehaviorSubject<T[]>} Cache which can be subscribed to
|
|
2477
|
-
*/
|
|
2478
|
-
sync(on = true) {
|
|
2479
|
-
if (on) {
|
|
2480
|
-
new _Slice(this.slice, this.api).select().rows().exec().then((rows) => this.cache = rows);
|
|
2481
|
-
if (!this.unsubscribe) this.unsubscribe = this.api.socket.sliceEvents(this.slice, (event) => {
|
|
2482
|
-
const ids = [...event.data.new, ...event.data.changed];
|
|
2483
|
-
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]);
|
|
2484
|
-
this.cache = this.cache.filter((v) => v.id && !event.data.lost.includes(v.id));
|
|
2485
|
-
});
|
|
2486
|
-
return this.cache$;
|
|
2487
|
-
} else if (this.unsubscribe) {
|
|
2488
|
-
this.unsubscribe();
|
|
2489
|
-
this.unsubscribe = null;
|
|
2490
|
-
}
|
|
2491
|
-
}
|
|
2492
2758
|
/**
|
|
2493
2759
|
* Set the request type to update
|
|
2494
2760
|
* @example
|
|
@@ -2531,6 +2797,7 @@ const _Slice = class _Slice {
|
|
|
2531
2797
|
Object.entries(field).forEach(([key, value2]) => this.where(key, "==", value2));
|
|
2532
2798
|
} else {
|
|
2533
2799
|
const w = raw ? field : [(() => {
|
|
2800
|
+
if (operator == null ? void 0 : operator.startsWith("$")) return operator;
|
|
2534
2801
|
if (operator == "==") return "$eq";
|
|
2535
2802
|
if (operator == "!=") return "$neq";
|
|
2536
2803
|
if (operator == ">") return "$gt";
|
|
@@ -2556,9 +2823,270 @@ const _Slice = class _Slice {
|
|
|
2556
2823
|
}
|
|
2557
2824
|
return this;
|
|
2558
2825
|
}
|
|
2559
|
-
}
|
|
2560
|
-
|
|
2561
|
-
|
|
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
|
+
}
|
|
2562
3090
|
class Socket {
|
|
2563
3091
|
constructor(api, options = {}) {
|
|
2564
3092
|
__publicField(this, "listeners", []);
|
|
@@ -2691,8 +3219,8 @@ class Superuser {
|
|
|
2691
3219
|
} });
|
|
2692
3220
|
}
|
|
2693
3221
|
}
|
|
2694
|
-
const version = "1.0
|
|
2695
|
-
class
|
|
3222
|
+
const version = "1.1.0-rc1";
|
|
3223
|
+
const _Api = class _Api {
|
|
2696
3224
|
/**
|
|
2697
3225
|
* Connect to Datalynk & send requests
|
|
2698
3226
|
*
|
|
@@ -2701,10 +3229,10 @@ class Api {
|
|
|
2701
3229
|
* const api = new Api('https://spoke.auxiliumgroup.com');
|
|
2702
3230
|
* ```
|
|
2703
3231
|
*
|
|
2704
|
-
* @param {string}
|
|
3232
|
+
* @param {string} origin API URL
|
|
2705
3233
|
* @param {ApiOptions} options
|
|
2706
3234
|
*/
|
|
2707
|
-
constructor(
|
|
3235
|
+
constructor(origin, options = {}) {
|
|
2708
3236
|
/** Current requests bundle */
|
|
2709
3237
|
__publicField(this, "bundle", []);
|
|
2710
3238
|
/** Bundle lifecycle tracking */
|
|
@@ -2713,12 +3241,6 @@ class Api {
|
|
|
2713
3241
|
__publicField(this, "localStorageKey", "datalynk-token");
|
|
2714
3242
|
/** Pending requests cache */
|
|
2715
3243
|
__publicField(this, "pending", {});
|
|
2716
|
-
/** API URL */
|
|
2717
|
-
__publicField(this, "url");
|
|
2718
|
-
/** Package version */
|
|
2719
|
-
__publicField(this, "version", version);
|
|
2720
|
-
/** API Session token */
|
|
2721
|
-
__publicField(this, "token$", new BehaviorSubject(void 0));
|
|
2722
3244
|
/** Helpers */
|
|
2723
3245
|
/** Authentication */
|
|
2724
3246
|
__publicField(this, "auth");
|
|
@@ -2730,12 +3252,26 @@ class Api {
|
|
|
2730
3252
|
__publicField(this, "socket");
|
|
2731
3253
|
/** Superuser */
|
|
2732
3254
|
__publicField(this, "superuser");
|
|
2733
|
-
|
|
2734
|
-
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/`;
|
|
2735
3270
|
this.options = {
|
|
2736
|
-
|
|
3271
|
+
offline: [],
|
|
2737
3272
|
origin: typeof location !== "undefined" ? location.host : "Unknown",
|
|
2738
3273
|
saveSession: true,
|
|
3274
|
+
serviceWorker: "/service.worker.js",
|
|
2739
3275
|
...options
|
|
2740
3276
|
};
|
|
2741
3277
|
if (this.options.saveSession) {
|
|
@@ -2746,12 +3282,32 @@ class Api {
|
|
|
2746
3282
|
else localStorage.removeItem(this.localStorageKey);
|
|
2747
3283
|
});
|
|
2748
3284
|
}
|
|
2749
|
-
|
|
3285
|
+
this.socket = new Socket(this, { url: options.socket });
|
|
2750
3286
|
this.auth = new Auth(this);
|
|
2751
3287
|
this.files = new Files(this);
|
|
2752
3288
|
this.pdf = new Pdf(this);
|
|
2753
3289
|
this.superuser = new Superuser(this);
|
|
2754
|
-
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;
|
|
2755
3311
|
}
|
|
2756
3312
|
get token() {
|
|
2757
3313
|
return this.token$.getValue();
|
|
@@ -2759,11 +3315,6 @@ class Api {
|
|
|
2759
3315
|
set token(token) {
|
|
2760
3316
|
this.token$.next(token);
|
|
2761
3317
|
}
|
|
2762
|
-
/** Get session info from JWT payload */
|
|
2763
|
-
get jwtPayload() {
|
|
2764
|
-
if (!this.token) return null;
|
|
2765
|
-
return decodeJwt(this.token);
|
|
2766
|
-
}
|
|
2767
3318
|
_request(req, options = {}) {
|
|
2768
3319
|
const token = options.token || this.token;
|
|
2769
3320
|
return fetch(this.url, {
|
|
@@ -2773,14 +3324,31 @@ class Api {
|
|
|
2773
3324
|
"Content-Type": "application/json",
|
|
2774
3325
|
"X-Date-Return-Format": this.options.legacyDates ? void 0 : "ISO8601"
|
|
2775
3326
|
}),
|
|
2776
|
-
body: JSON.stringify(
|
|
3327
|
+
body: JSON.stringify(_Api.translateTokens(req))
|
|
2777
3328
|
}).then(async (resp) => {
|
|
2778
3329
|
let data = JSONAttemptParse(await resp.text());
|
|
2779
3330
|
if (!resp.ok || (data == null ? void 0 : data.error)) throw Object.assign(errorFromCode(resp.status, data.error), data);
|
|
2780
|
-
if (!options.raw) data =
|
|
3331
|
+
if (!options.raw) data = _Api.translateTokens(data);
|
|
2781
3332
|
return data;
|
|
2782
3333
|
});
|
|
2783
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
|
+
}
|
|
2784
3352
|
/**
|
|
2785
3353
|
* Parses API request/response object for special Datalynk tokens & converts them to native JS objects
|
|
2786
3354
|
*
|
|
@@ -2799,6 +3367,10 @@ class Api {
|
|
|
2799
3367
|
val = Serializer.deserialize[index](val[index]);
|
|
2800
3368
|
} else if (index in Serializer.serialize) {
|
|
2801
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;
|
|
2802
3374
|
} else {
|
|
2803
3375
|
queue.push(`${key}.${index}`);
|
|
2804
3376
|
}
|
|
@@ -2815,7 +3387,7 @@ class Api {
|
|
|
2815
3387
|
chain(...requests) {
|
|
2816
3388
|
const arr = requests.length == 1 && Array.isArray(requests[0]) ? requests[0] : requests;
|
|
2817
3389
|
return this.request({ "$/tools/action_chain": arr.map((r) => {
|
|
2818
|
-
if (!(r instanceof
|
|
3390
|
+
if (!(r instanceof ApiCall)) return r;
|
|
2819
3391
|
const req = r.raw;
|
|
2820
3392
|
Object.keys(req).forEach((key) => {
|
|
2821
3393
|
if (key.startsWith("$/")) {
|
|
@@ -2834,7 +3406,7 @@ class Api {
|
|
|
2834
3406
|
chainMap(request) {
|
|
2835
3407
|
return this.request({ "$/tools/do": [
|
|
2836
3408
|
...Object.entries(request).flatMap(([key, r]) => {
|
|
2837
|
-
if (!(r instanceof
|
|
3409
|
+
if (!(r instanceof ApiCall)) return [key, r];
|
|
2838
3410
|
const req = r.raw;
|
|
2839
3411
|
Object.keys(req).forEach((key2) => {
|
|
2840
3412
|
if (key2.startsWith("$/")) {
|
|
@@ -2915,15 +3487,20 @@ class Api {
|
|
|
2915
3487
|
* const unsubscribe = contactsSlice.sync().subscribe(rows => console.log(rows));
|
|
2916
3488
|
* ```
|
|
2917
3489
|
*
|
|
2918
|
-
* @param {number}
|
|
3490
|
+
* @param {number} id Slice ID the object will target
|
|
2919
3491
|
* @returns {Slice<T = any>} Object for making requests & caching rows
|
|
2920
3492
|
*/
|
|
2921
|
-
slice(
|
|
2922
|
-
|
|
3493
|
+
slice(id) {
|
|
3494
|
+
if (!this.sliceCache.has(+id)) this.sliceCache.set(+id, new Slice(id, this));
|
|
3495
|
+
return this.sliceCache.get(+id);
|
|
2923
3496
|
}
|
|
2924
|
-
}
|
|
3497
|
+
};
|
|
3498
|
+
/** Client library version */
|
|
3499
|
+
__publicField(_Api, "version", version);
|
|
3500
|
+
let Api = _Api;
|
|
2925
3501
|
export {
|
|
2926
3502
|
Api,
|
|
3503
|
+
ApiCall,
|
|
2927
3504
|
Auth,
|
|
2928
3505
|
Files,
|
|
2929
3506
|
LoginPrompt,
|
|
@@ -2931,5 +3508,6 @@ export {
|
|
|
2931
3508
|
Serializer,
|
|
2932
3509
|
Slice,
|
|
2933
3510
|
Socket,
|
|
2934
|
-
Superuser
|
|
3511
|
+
Superuser,
|
|
3512
|
+
getTheme
|
|
2935
3513
|
};
|