@auxilium/datalynk-client 1.4.0 → 1.5.0
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/README.md +36 -0
- package/bin/datalynk-migrate.mjs +86 -0
- package/bin/datalynk-models.mjs +3 -3
- package/bin/utils.mjs +163 -0
- package/dist/api.d.ts +96 -5
- package/dist/api.d.ts.map +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/gps.d.ts +34 -2
- package/dist/gps.d.ts.map +1 -1
- package/dist/index.cjs +600 -857
- package/dist/index.mjs +598 -855
- package/dist/login-prompt.d.ts +4 -1
- package/dist/login-prompt.d.ts.map +1 -1
- package/dist/slice.d.ts +22 -5
- package/dist/slice.d.ts.map +1 -1
- package/dist/themes.d.ts +1 -1
- package/dist/themes.d.ts.map +1 -1
- package/dist/utils.d.ts +95 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/webrtc.d.ts +1 -1
- package/dist/webrtc.d.ts.map +1 -1
- package/package.json +11 -7
package/dist/index.mjs
CHANGED
|
@@ -1,898 +1,272 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} else {
|
|
12
|
-
Object.entries(obj).forEach(([key, value]) => {
|
|
13
|
-
if (undefinedOnly && value === void 0 || !undefinedOnly && value == null) delete obj[key];
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
return obj;
|
|
4
|
+
function clean(value, undefinedOnly = false) {
|
|
5
|
+
if (value == null) throw new Error("Cannot clean a NULL value");
|
|
6
|
+
if (Array.isArray(value)) return value.filter((item) => undefinedOnly ? item !== void 0 : item != null);
|
|
7
|
+
Object.entries(value).forEach(([key, item]) => {
|
|
8
|
+
if (undefinedOnly && item === void 0 || !undefinedOnly && item == null) delete value[key];
|
|
9
|
+
});
|
|
10
|
+
return value;
|
|
17
11
|
}
|
|
18
12
|
function deepCopy(value) {
|
|
19
13
|
try {
|
|
20
14
|
return structuredClone(value);
|
|
21
15
|
} catch {
|
|
22
|
-
|
|
16
|
+
const seen = [];
|
|
17
|
+
return JSON.parse(JSON.stringify(value, (_key, item) => {
|
|
18
|
+
if (typeof item === "object" && item !== null) {
|
|
19
|
+
if (seen.includes(item)) return "[Circular]";
|
|
20
|
+
seen.push(item);
|
|
21
|
+
}
|
|
22
|
+
return item;
|
|
23
|
+
}));
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
|
-
function
|
|
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
|
-
}
|
|
43
|
-
function JSONAttemptParse(json) {
|
|
26
|
+
function JSONAttemptParse(value) {
|
|
44
27
|
try {
|
|
45
|
-
return JSON.parse(
|
|
28
|
+
return JSON.parse(value);
|
|
46
29
|
} catch {
|
|
47
|
-
return json;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function JSONSanitize(obj, space) {
|
|
51
|
-
const cache = [];
|
|
52
|
-
return JSON.stringify(obj, (key, value) => {
|
|
53
|
-
if (typeof value === "object" && value !== null) {
|
|
54
|
-
if (cache.includes(value)) return "[Circular]";
|
|
55
|
-
cache.push(value);
|
|
56
|
-
}
|
|
57
30
|
return value;
|
|
58
|
-
}, space);
|
|
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
31
|
}
|
|
174
32
|
}
|
|
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
33
|
function makeArray(value) {
|
|
187
34
|
return Array.isArray(value) ? value : [value];
|
|
188
35
|
}
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
}
|
|
36
|
+
function property(value, path) {
|
|
37
|
+
return path.split(".").reduce((current, key) => current == null ? void 0 : current[key], value);
|
|
227
38
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
}
|
|
39
|
+
function sortByProp(path, reverse = false) {
|
|
40
|
+
return (a, b) => {
|
|
41
|
+
const aValue = property(a, path);
|
|
42
|
+
const bValue = property(b, path);
|
|
43
|
+
if (typeof aValue == "number" && typeof bValue == "number")
|
|
44
|
+
return (reverse ? -1 : 1) * (aValue - bValue);
|
|
45
|
+
if (aValue > bValue) return reverse ? -1 : 1;
|
|
46
|
+
if (aValue < bValue) return reverse ? 1 : -1;
|
|
47
|
+
return 0;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function sleepWhile(condition, interval = 100) {
|
|
51
|
+
while (await condition()) await new Promise((resolve) => setTimeout(resolve, interval));
|
|
267
52
|
}
|
|
268
53
|
function contrast(background) {
|
|
269
|
-
const
|
|
270
|
-
if (!
|
|
271
|
-
const [
|
|
272
|
-
|
|
273
|
-
return luminance > 0.5 ? "black" : "white";
|
|
54
|
+
const parts = background == null ? void 0 : background.match(background.length >= 6 ? /[0-9a-fA-F]{2}/g : /[0-9a-fA-F]/g);
|
|
55
|
+
if (!parts || parts.length < 3) return "black";
|
|
56
|
+
const [red, green, blue] = parts.map((hex) => parseInt(hex.length === 1 ? hex + hex : hex, 16));
|
|
57
|
+
return (0.299 * red + 0.587 * green + 0.114 * blue) / 255 > 0.5 ? "black" : "white";
|
|
274
58
|
}
|
|
275
|
-
const LETTER_LIST = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
276
|
-
const NUMBER_LIST = "0123456789";
|
|
277
|
-
const SYMBOL_LIST = "~`!@#$%^&*()_-+={[}]|\\:;\"'<,>.?/";
|
|
278
59
|
function randomStringBuilder(length, letters = false, numbers = false, symbols = false) {
|
|
60
|
+
const LETTER_LIST = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
61
|
+
const NUMBER_LIST = "0123456789";
|
|
62
|
+
const SYMBOL_LIST = "~`!@#$%^&*()_-+={[}]|\\:;\"'<,>.?/";
|
|
279
63
|
if (!letters && !numbers && !symbols) throw new Error("Must enable at least one: letters, numbers, symbols");
|
|
280
64
|
return Array(length).fill(null).map(() => {
|
|
281
|
-
let
|
|
65
|
+
let character;
|
|
282
66
|
do {
|
|
283
67
|
const type = ~~(Math.random() * 3);
|
|
284
|
-
if (letters && type == 0)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
c = SYMBOL_LIST[~~(Math.random() * SYMBOL_LIST.length)];
|
|
290
|
-
}
|
|
291
|
-
} while (!c);
|
|
292
|
-
return c;
|
|
68
|
+
if (letters && type == 0) character = LETTER_LIST[~~(Math.random() * LETTER_LIST.length)];
|
|
69
|
+
else if (numbers && type == 1) character = NUMBER_LIST[~~(Math.random() * NUMBER_LIST.length)];
|
|
70
|
+
else if (symbols && type == 2) character = SYMBOL_LIST[~~(Math.random() * SYMBOL_LIST.length)];
|
|
71
|
+
} while (!character);
|
|
72
|
+
return character;
|
|
293
73
|
}).join("");
|
|
294
74
|
}
|
|
295
|
-
class PromiseProgress extends Promise {
|
|
296
|
-
constructor(executor) {
|
|
297
|
-
super((resolve, reject) => executor(
|
|
298
|
-
(value) => resolve(value),
|
|
299
|
-
(reason) => reject(reason),
|
|
300
|
-
(progress) => this.progress = progress
|
|
301
|
-
));
|
|
302
|
-
__publicField2(this, "listeners", []);
|
|
303
|
-
__publicField2(this, "_progress", 0);
|
|
304
|
-
}
|
|
305
|
-
get progress() {
|
|
306
|
-
return this._progress;
|
|
307
|
-
}
|
|
308
|
-
set progress(p) {
|
|
309
|
-
if (p == this._progress) return;
|
|
310
|
-
this._progress = p;
|
|
311
|
-
this.listeners.forEach((l) => l(p));
|
|
312
|
-
}
|
|
313
|
-
static from(promise) {
|
|
314
|
-
if (promise instanceof PromiseProgress) return promise;
|
|
315
|
-
return new PromiseProgress((res, rej) => promise.then((...args) => res(...args)).catch((...args) => rej(...args)));
|
|
316
|
-
}
|
|
317
|
-
from(promise) {
|
|
318
|
-
const newPromise = PromiseProgress.from(promise);
|
|
319
|
-
this.onProgress((p) => newPromise.progress = p);
|
|
320
|
-
return newPromise;
|
|
321
|
-
}
|
|
322
|
-
onProgress(callback) {
|
|
323
|
-
this.listeners.push(callback);
|
|
324
|
-
return this;
|
|
325
|
-
}
|
|
326
|
-
then(res, rej) {
|
|
327
|
-
const resp = super.then(res, rej);
|
|
328
|
-
return this.from(resp);
|
|
329
|
-
}
|
|
330
|
-
catch(rej) {
|
|
331
|
-
return this.from(super.catch(rej));
|
|
332
|
-
}
|
|
333
|
-
finally(res) {
|
|
334
|
-
return this.from(super.finally(res));
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
function sleep(ms) {
|
|
338
|
-
return new Promise((res) => setTimeout(res, ms));
|
|
339
|
-
}
|
|
340
|
-
async function sleepWhile(fn2, checkInterval = 100) {
|
|
341
|
-
while (await fn2()) await sleep(checkInterval);
|
|
342
|
-
}
|
|
343
|
-
class TypedEmitter {
|
|
344
|
-
constructor() {
|
|
345
|
-
__publicField2(this, "listeners", {});
|
|
346
|
-
}
|
|
347
|
-
static emit(event, ...args) {
|
|
348
|
-
(this.listeners["*"] || []).forEach((l) => l(event, ...args));
|
|
349
|
-
(this.listeners[event.toString()] || []).forEach((l) => l(...args));
|
|
350
|
-
}
|
|
351
|
-
static off(event, listener) {
|
|
352
|
-
const e = event.toString();
|
|
353
|
-
this.listeners[e] = (this.listeners[e] || []).filter((l) => l != listener);
|
|
354
|
-
}
|
|
355
|
-
static on(event, listener) {
|
|
356
|
-
var _a;
|
|
357
|
-
const e = event.toString();
|
|
358
|
-
if (!this.listeners[e]) this.listeners[e] = [];
|
|
359
|
-
(_a = this.listeners[e]) == null ? void 0 : _a.push(listener);
|
|
360
|
-
return () => this.off(event, listener);
|
|
361
|
-
}
|
|
362
|
-
static once(event, listener) {
|
|
363
|
-
return new Promise((res) => {
|
|
364
|
-
const unsubscribe = this.on(event, (...args) => {
|
|
365
|
-
res(args.length == 1 ? args[0] : args);
|
|
366
|
-
if (listener) listener(...args);
|
|
367
|
-
unsubscribe();
|
|
368
|
-
});
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
emit(event, ...args) {
|
|
372
|
-
(this.listeners["*"] || []).forEach((l) => l(event, ...args));
|
|
373
|
-
(this.listeners[event] || []).forEach((l) => l(...args));
|
|
374
|
-
}
|
|
375
|
-
off(event, listener) {
|
|
376
|
-
this.listeners[event] = (this.listeners[event] || []).filter((l) => l != listener);
|
|
377
|
-
}
|
|
378
|
-
on(event, listener) {
|
|
379
|
-
var _a;
|
|
380
|
-
if (!this.listeners[event]) this.listeners[event] = [];
|
|
381
|
-
(_a = this.listeners[event]) == null ? void 0 : _a.push(listener);
|
|
382
|
-
return () => this.off(event, listener);
|
|
383
|
-
}
|
|
384
|
-
once(event, listener) {
|
|
385
|
-
return new Promise((res) => {
|
|
386
|
-
const unsubscribe = this.on(event, (...args) => {
|
|
387
|
-
res(args.length == 1 ? args[0] : args);
|
|
388
|
-
if (listener) listener(...args);
|
|
389
|
-
unsubscribe();
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
__publicField2(TypedEmitter, "listeners", {});
|
|
395
75
|
class CustomError extends Error {
|
|
396
76
|
constructor(message, code) {
|
|
397
77
|
super(message);
|
|
398
|
-
|
|
78
|
+
__publicField(this, "_code");
|
|
399
79
|
if (code != null) this._code = code;
|
|
400
80
|
}
|
|
401
81
|
get code() {
|
|
402
82
|
return this._code || this.constructor.code;
|
|
403
83
|
}
|
|
404
|
-
set code(
|
|
405
|
-
this._code =
|
|
406
|
-
}
|
|
407
|
-
static from(err) {
|
|
408
|
-
const code = Number(err.statusCode) ?? Number(err.code);
|
|
409
|
-
const newErr = new this(err.message || err.toString());
|
|
410
|
-
return Object.assign(newErr, {
|
|
411
|
-
stack: err.stack,
|
|
412
|
-
...err,
|
|
413
|
-
code: code ?? void 0
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
static instanceof(err) {
|
|
417
|
-
return err.constructor.code != void 0;
|
|
84
|
+
set code(code) {
|
|
85
|
+
this._code = code;
|
|
418
86
|
}
|
|
419
87
|
toString() {
|
|
420
88
|
return this.message || super.toString();
|
|
421
89
|
}
|
|
422
90
|
}
|
|
423
|
-
|
|
91
|
+
__publicField(CustomError, "code", 500);
|
|
424
92
|
class BadRequestError extends CustomError {
|
|
425
93
|
constructor(message = "Bad Request") {
|
|
426
94
|
super(message);
|
|
427
95
|
}
|
|
428
|
-
static instanceof(err) {
|
|
429
|
-
return err.constructor.code == this.code;
|
|
430
|
-
}
|
|
431
96
|
}
|
|
432
|
-
|
|
97
|
+
__publicField(BadRequestError, "code", 400);
|
|
433
98
|
class UnauthorizedError extends CustomError {
|
|
434
99
|
constructor(message = "Unauthorized") {
|
|
435
100
|
super(message);
|
|
436
101
|
}
|
|
437
|
-
static instanceof(err) {
|
|
438
|
-
return err.constructor.code == this.code;
|
|
439
|
-
}
|
|
440
102
|
}
|
|
441
|
-
|
|
103
|
+
__publicField(UnauthorizedError, "code", 401);
|
|
442
104
|
class PaymentRequiredError extends CustomError {
|
|
443
105
|
constructor(message = "Payment Required") {
|
|
444
106
|
super(message);
|
|
445
107
|
}
|
|
446
|
-
static instanceof(err) {
|
|
447
|
-
return err.constructor.code == this.code;
|
|
448
|
-
}
|
|
449
108
|
}
|
|
450
|
-
|
|
109
|
+
__publicField(PaymentRequiredError, "code", 402);
|
|
451
110
|
class ForbiddenError extends CustomError {
|
|
452
111
|
constructor(message = "Forbidden") {
|
|
453
112
|
super(message);
|
|
454
113
|
}
|
|
455
|
-
static instanceof(err) {
|
|
456
|
-
return err.constructor.code == this.code;
|
|
457
|
-
}
|
|
458
114
|
}
|
|
459
|
-
|
|
115
|
+
__publicField(ForbiddenError, "code", 403);
|
|
460
116
|
class NotFoundError extends CustomError {
|
|
461
117
|
constructor(message = "Not Found") {
|
|
462
118
|
super(message);
|
|
463
119
|
}
|
|
464
|
-
static instanceof(err) {
|
|
465
|
-
return err.constructor.code == this.code;
|
|
466
|
-
}
|
|
467
120
|
}
|
|
468
|
-
|
|
121
|
+
__publicField(NotFoundError, "code", 404);
|
|
469
122
|
class MethodNotAllowedError extends CustomError {
|
|
470
123
|
constructor(message = "Method Not Allowed") {
|
|
471
124
|
super(message);
|
|
472
125
|
}
|
|
473
|
-
static instanceof(err) {
|
|
474
|
-
return err.constructor.code == this.code;
|
|
475
|
-
}
|
|
476
126
|
}
|
|
477
|
-
|
|
127
|
+
__publicField(MethodNotAllowedError, "code", 405);
|
|
478
128
|
class NotAcceptableError extends CustomError {
|
|
479
129
|
constructor(message = "Not Acceptable") {
|
|
480
130
|
super(message);
|
|
481
131
|
}
|
|
482
|
-
static instanceof(err) {
|
|
483
|
-
return err.constructor.code == this.code;
|
|
484
|
-
}
|
|
485
132
|
}
|
|
486
|
-
|
|
133
|
+
__publicField(NotAcceptableError, "code", 406);
|
|
487
134
|
class InternalServerError extends CustomError {
|
|
488
135
|
constructor(message = "Internal Server Error") {
|
|
489
136
|
super(message);
|
|
490
137
|
}
|
|
491
|
-
static instanceof(err) {
|
|
492
|
-
return err.constructor.code == this.code;
|
|
493
|
-
}
|
|
494
138
|
}
|
|
495
|
-
|
|
139
|
+
__publicField(InternalServerError, "code", 500);
|
|
496
140
|
class NotImplementedError extends CustomError {
|
|
497
141
|
constructor(message = "Not Implemented") {
|
|
498
142
|
super(message);
|
|
499
143
|
}
|
|
500
|
-
static instanceof(err) {
|
|
501
|
-
return err.constructor.code == this.code;
|
|
502
|
-
}
|
|
503
144
|
}
|
|
504
|
-
|
|
145
|
+
__publicField(NotImplementedError, "code", 501);
|
|
505
146
|
class BadGatewayError extends CustomError {
|
|
506
147
|
constructor(message = "Bad Gateway") {
|
|
507
148
|
super(message);
|
|
508
149
|
}
|
|
509
|
-
static instanceof(err) {
|
|
510
|
-
return err.constructor.code == this.code;
|
|
511
|
-
}
|
|
512
150
|
}
|
|
513
|
-
|
|
151
|
+
__publicField(BadGatewayError, "code", 502);
|
|
514
152
|
class ServiceUnavailableError extends CustomError {
|
|
515
153
|
constructor(message = "Service Unavailable") {
|
|
516
154
|
super(message);
|
|
517
155
|
}
|
|
518
|
-
static instanceof(err) {
|
|
519
|
-
return err.constructor.code == this.code;
|
|
520
|
-
}
|
|
521
156
|
}
|
|
522
|
-
|
|
157
|
+
__publicField(ServiceUnavailableError, "code", 503);
|
|
523
158
|
class GatewayTimeoutError extends CustomError {
|
|
524
159
|
constructor(message = "Gateway Timeout") {
|
|
525
160
|
super(message);
|
|
526
161
|
}
|
|
527
|
-
static instanceof(err) {
|
|
528
|
-
return err.constructor.code == this.code;
|
|
529
|
-
}
|
|
530
162
|
}
|
|
531
|
-
|
|
163
|
+
__publicField(GatewayTimeoutError, "code", 504);
|
|
532
164
|
function errorFromCode(code, message) {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
return new InternalServerError(message);
|
|
550
|
-
case 501:
|
|
551
|
-
return new NotImplementedError(message);
|
|
552
|
-
case 502:
|
|
553
|
-
return new BadGatewayError(message);
|
|
554
|
-
case 503:
|
|
555
|
-
return new ServiceUnavailableError(message);
|
|
556
|
-
case 504:
|
|
557
|
-
return new GatewayTimeoutError(message);
|
|
558
|
-
default:
|
|
559
|
-
return new CustomError(message, code);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
class HttpResponse extends Response {
|
|
563
|
-
constructor(resp, stream) {
|
|
564
|
-
const body = [204, 205, 304].includes(resp.status) ? null : stream;
|
|
565
|
-
super(body, {
|
|
566
|
-
headers: resp.headers,
|
|
567
|
-
status: resp.status,
|
|
568
|
-
statusText: resp.statusText
|
|
569
|
-
});
|
|
570
|
-
__publicField2(this, "data");
|
|
571
|
-
__publicField2(this, "ok");
|
|
572
|
-
__publicField2(this, "redirected");
|
|
573
|
-
__publicField2(this, "type");
|
|
574
|
-
__publicField2(this, "url");
|
|
575
|
-
this.ok = resp.ok;
|
|
576
|
-
this.redirected = resp.redirected;
|
|
577
|
-
this.type = resp.type;
|
|
578
|
-
this.url = resp.url;
|
|
579
|
-
}
|
|
165
|
+
const errors = {
|
|
166
|
+
400: BadRequestError,
|
|
167
|
+
401: UnauthorizedError,
|
|
168
|
+
402: PaymentRequiredError,
|
|
169
|
+
403: ForbiddenError,
|
|
170
|
+
404: NotFoundError,
|
|
171
|
+
405: MethodNotAllowedError,
|
|
172
|
+
406: NotAcceptableError,
|
|
173
|
+
500: InternalServerError,
|
|
174
|
+
501: NotImplementedError,
|
|
175
|
+
502: BadGatewayError,
|
|
176
|
+
503: ServiceUnavailableError,
|
|
177
|
+
504: GatewayTimeoutError
|
|
178
|
+
};
|
|
179
|
+
const ErrorType = errors[code];
|
|
180
|
+
return ErrorType ? new ErrorType(message) : new CustomError(message, code);
|
|
580
181
|
}
|
|
581
|
-
const _Http = class _Http2 {
|
|
582
|
-
constructor(defaults = {}) {
|
|
583
|
-
__publicField2(this, "interceptors", {});
|
|
584
|
-
__publicField2(this, "headers", {});
|
|
585
|
-
__publicField2(this, "url");
|
|
586
|
-
this.url = defaults.url ?? null;
|
|
587
|
-
this.headers = defaults.headers || {};
|
|
588
|
-
if (defaults.interceptors) {
|
|
589
|
-
defaults.interceptors.forEach((i) => _Http2.addInterceptor(i));
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
static addInterceptor(fn2) {
|
|
593
|
-
const key = Object.keys(_Http2.interceptors).length.toString();
|
|
594
|
-
_Http2.interceptors[key] = fn2;
|
|
595
|
-
return () => {
|
|
596
|
-
_Http2.interceptors[key] = null;
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
addInterceptor(fn2) {
|
|
600
|
-
const key = Object.keys(this.interceptors).length.toString();
|
|
601
|
-
this.interceptors[key] = fn2;
|
|
602
|
-
return () => {
|
|
603
|
-
this.interceptors[key] = null;
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
request(opts = {}) {
|
|
607
|
-
var _a;
|
|
608
|
-
if (!this.url && !opts.url) throw new Error("URL needs to be set");
|
|
609
|
-
let url = ((_a = opts.url) == null ? void 0 : _a.startsWith("http")) ? opts.url : (this.url || "") + (opts.url || "");
|
|
610
|
-
url = url.replaceAll(/([^:]\/)\/+/g, "$1");
|
|
611
|
-
if (opts.fragment) url.includes("#") ? url.replace(/#.*(\?|\n)/g, (match, arg1) => `#${opts.fragment}${arg1}`) : `${url}#${opts.fragment}`;
|
|
612
|
-
if (opts.query) {
|
|
613
|
-
const q = Array.isArray(opts.query) ? opts.query : Object.keys(opts.query).map((k) => ({ key: k, value: opts.query[k] }));
|
|
614
|
-
url += (url.includes("?") ? "&" : "?") + q.map((q2) => `${q2.key}=${q2.value}`).join("&");
|
|
615
|
-
}
|
|
616
|
-
const headers = clean({
|
|
617
|
-
"Content-Type": !opts.body ? void 0 : opts.body instanceof FormData ? "multipart/form-data" : "application/json",
|
|
618
|
-
..._Http2.headers,
|
|
619
|
-
...this.headers,
|
|
620
|
-
...opts.headers
|
|
621
|
-
});
|
|
622
|
-
if (typeof opts.body == "object" && opts.body != null && headers["Content-Type"] == "application/json")
|
|
623
|
-
opts.body = JSON.stringify(opts.body);
|
|
624
|
-
return new PromiseProgress((res, rej, prog) => {
|
|
625
|
-
try {
|
|
626
|
-
fetch(url, {
|
|
627
|
-
headers,
|
|
628
|
-
method: opts.method || (opts.body ? "POST" : "GET"),
|
|
629
|
-
body: opts.body
|
|
630
|
-
}).then(async (resp) => {
|
|
631
|
-
var _a2, _b;
|
|
632
|
-
for (let fn2 of [...Object.values(_Http2.interceptors), ...Object.values(this.interceptors)]) {
|
|
633
|
-
await new Promise((res2) => fn2(resp, () => res2()));
|
|
634
|
-
}
|
|
635
|
-
const contentLength = resp.headers.get("Content-Length");
|
|
636
|
-
const total = contentLength ? parseInt(contentLength, 10) : 0;
|
|
637
|
-
let loaded = 0;
|
|
638
|
-
const reader = (_a2 = resp.body) == null ? void 0 : _a2.getReader();
|
|
639
|
-
const stream = new ReadableStream({
|
|
640
|
-
start(controller) {
|
|
641
|
-
function push() {
|
|
642
|
-
reader == null ? void 0 : reader.read().then((event) => {
|
|
643
|
-
if (event.done) return controller.close();
|
|
644
|
-
loaded += event.value.byteLength;
|
|
645
|
-
prog(loaded / total);
|
|
646
|
-
controller.enqueue(event.value);
|
|
647
|
-
push();
|
|
648
|
-
}).catch((error) => controller.error(error));
|
|
649
|
-
}
|
|
650
|
-
push();
|
|
651
|
-
}
|
|
652
|
-
});
|
|
653
|
-
resp = new HttpResponse(resp, stream);
|
|
654
|
-
if (opts.decode !== false) {
|
|
655
|
-
const content = (_b = resp.headers.get("Content-Type")) == null ? void 0 : _b.toLowerCase();
|
|
656
|
-
if (content == null ? void 0 : content.includes("form")) resp.data = await resp.formData();
|
|
657
|
-
else if (content == null ? void 0 : content.includes("json")) resp.data = await resp.json();
|
|
658
|
-
else if (content == null ? void 0 : content.includes("text")) resp.data = await resp.text();
|
|
659
|
-
else if (content == null ? void 0 : content.includes("application")) resp.data = await resp.blob();
|
|
660
|
-
}
|
|
661
|
-
if (resp.ok) res(resp);
|
|
662
|
-
else rej(resp);
|
|
663
|
-
}).catch((err) => rej(err));
|
|
664
|
-
} catch (err) {
|
|
665
|
-
rej(err);
|
|
666
|
-
}
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
|
-
};
|
|
670
|
-
__publicField2(_Http, "interceptors", {});
|
|
671
|
-
__publicField2(_Http, "headers", {});
|
|
672
182
|
function decodeJwt(token) {
|
|
673
183
|
const base64 = token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
674
|
-
return JSONAttemptParse(decodeURIComponent(atob(base64).split("").map(
|
|
675
|
-
|
|
676
|
-
|
|
184
|
+
return JSONAttemptParse(decodeURIComponent(atob(base64).split("").map(
|
|
185
|
+
(character) => "%" + ("00" + character.charCodeAt(0).toString(16)).slice(-2)
|
|
186
|
+
).join("")));
|
|
677
187
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
console.debug(CliForeground.LIGHT_GREY + str + CliEffects.CLEAR);
|
|
702
|
-
}
|
|
703
|
-
log(...args) {
|
|
704
|
-
if (_Logger2.LOG_LEVEL < 3) return;
|
|
705
|
-
const str = this.format(...args);
|
|
706
|
-
_Logger2.emit(3, str);
|
|
707
|
-
console.log(CliEffects.CLEAR + str);
|
|
708
|
-
}
|
|
709
|
-
info(...args) {
|
|
710
|
-
if (_Logger2.LOG_LEVEL < 2) return;
|
|
711
|
-
const str = this.format(...args);
|
|
712
|
-
_Logger2.emit(2, str);
|
|
713
|
-
console.info(CliForeground.BLUE + str + CliEffects.CLEAR);
|
|
714
|
-
}
|
|
715
|
-
warn(...args) {
|
|
716
|
-
if (_Logger2.LOG_LEVEL < 1) return;
|
|
717
|
-
const str = this.format(...args);
|
|
718
|
-
_Logger2.emit(1, str);
|
|
719
|
-
console.warn(CliForeground.YELLOW + str + CliEffects.CLEAR);
|
|
720
|
-
}
|
|
721
|
-
error(...args) {
|
|
722
|
-
if (_Logger2.LOG_LEVEL < 0) return;
|
|
723
|
-
const str = this.format(...args);
|
|
724
|
-
_Logger2.emit(0, str);
|
|
725
|
-
console.error(CliForeground.RED + str + CliEffects.CLEAR);
|
|
726
|
-
}
|
|
727
|
-
};
|
|
728
|
-
__publicField2(_Logger, "LOG_LEVEL", 4);
|
|
729
|
-
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
|
730
|
-
var dist = {};
|
|
731
|
-
var persist$1 = {};
|
|
732
|
-
Object.defineProperty(persist$1, "__esModule", { value: true });
|
|
733
|
-
persist$1.persist = persist$1.Persist = void 0;
|
|
734
|
-
class Persist {
|
|
735
|
-
/**
|
|
736
|
-
* @param {string} key Primary key value will be stored under
|
|
737
|
-
* @param {PersistOptions<T>} options Configure using {@link PersistOptions}
|
|
738
|
-
*/
|
|
739
|
-
constructor(key, options = {}) {
|
|
740
|
-
__publicField2(this, "key");
|
|
741
|
-
__publicField2(this, "options");
|
|
742
|
-
__publicField2(this, "storage");
|
|
743
|
-
__publicField2(this, "watches", {});
|
|
744
|
-
__publicField2(this, "_value");
|
|
745
|
-
this.key = key;
|
|
746
|
-
this.options = options;
|
|
747
|
-
this.storage = options.storage || localStorage;
|
|
748
|
-
this.load();
|
|
749
|
-
}
|
|
750
|
-
/** Current value or default if undefined */
|
|
751
|
-
get value() {
|
|
752
|
-
var _a;
|
|
753
|
-
return this._value !== void 0 ? this._value : (_a = this.options) == null ? void 0 : _a.default;
|
|
754
|
-
}
|
|
755
|
-
/** Set value with proxy object wrapper to sync future changes */
|
|
756
|
-
set value(v) {
|
|
757
|
-
if (v == null || typeof v != "object")
|
|
758
|
-
this._value = v;
|
|
759
|
-
else
|
|
760
|
-
this._value = new Proxy(v, {
|
|
761
|
-
get: (target, p) => {
|
|
762
|
-
const f = typeof target[p] == "function";
|
|
763
|
-
if (!f)
|
|
764
|
-
return target[p];
|
|
765
|
-
return (...args) => {
|
|
766
|
-
const value = target[p](...args);
|
|
767
|
-
this.save();
|
|
768
|
-
return value;
|
|
769
|
-
};
|
|
770
|
-
},
|
|
771
|
-
set: (target, p, newValue) => {
|
|
772
|
-
target[p] = newValue;
|
|
773
|
-
this.save();
|
|
774
|
-
return true;
|
|
188
|
+
class Database {
|
|
189
|
+
constructor(database, tables, version2) {
|
|
190
|
+
__publicField(this, "connection");
|
|
191
|
+
__publicField(this, "tables");
|
|
192
|
+
this.database = database;
|
|
193
|
+
this.version = version2;
|
|
194
|
+
this.tables = tables.map((table) => typeof table === "object" ? { ...table, name: table.name.toString() } : { name: table.toString() });
|
|
195
|
+
this.connection = new Promise((resolve, reject) => {
|
|
196
|
+
const request = indexedDB.open(this.database, this.version);
|
|
197
|
+
const requested = new Set(this.tables.map((table) => table.name));
|
|
198
|
+
request.onerror = () => reject(request.error);
|
|
199
|
+
request.onsuccess = () => {
|
|
200
|
+
const db = request.result;
|
|
201
|
+
const existing = new Set(Array.from(db.objectStoreNames));
|
|
202
|
+
if ([...requested].some((name) => !existing.has(name)) || [...existing].some((name) => !requested.has(name))) {
|
|
203
|
+
db.close();
|
|
204
|
+
const upgraded = new Database(this.database, this.tables, db.version + 1);
|
|
205
|
+
this.version = upgraded.version;
|
|
206
|
+
this.connection = upgraded.connection;
|
|
207
|
+
this.connection.then(resolve, reject);
|
|
208
|
+
} else {
|
|
209
|
+
this.version = db.version;
|
|
210
|
+
resolve(db);
|
|
775
211
|
}
|
|
776
|
-
}
|
|
777
|
-
|
|
212
|
+
};
|
|
213
|
+
request.onupgradeneeded = () => {
|
|
214
|
+
const db = request.result;
|
|
215
|
+
const existing = new Set(Array.from(db.objectStoreNames));
|
|
216
|
+
existing.forEach((name) => {
|
|
217
|
+
if (!requested.has(name)) db.deleteObjectStore(name);
|
|
218
|
+
});
|
|
219
|
+
requested.forEach((name) => {
|
|
220
|
+
if (!existing.has(name)) db.createObjectStore(name);
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
});
|
|
778
224
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
Object.values(this.watches).forEach((watch) => watch(value));
|
|
225
|
+
includes(name) {
|
|
226
|
+
return this.tables.some((table) => table.name === name.toString());
|
|
782
227
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
this.storage.removeItem(this.key);
|
|
786
|
-
}
|
|
787
|
-
/** Save current value to storage */
|
|
788
|
-
save() {
|
|
789
|
-
if (this._value === void 0)
|
|
790
|
-
this.clear();
|
|
791
|
-
else
|
|
792
|
-
this.storage.setItem(this.key, JSON.stringify(this._value));
|
|
793
|
-
this.notify(this.value);
|
|
794
|
-
}
|
|
795
|
-
/** Load value from storage */
|
|
796
|
-
load() {
|
|
797
|
-
if (this.storage[this.key] != void 0) {
|
|
798
|
-
let value = JSON.parse(this.storage.getItem(this.key));
|
|
799
|
-
if (value != null && typeof value == "object" && this.options.type)
|
|
800
|
-
value.__proto__ = this.options.type.prototype;
|
|
801
|
-
this.value = value;
|
|
802
|
-
} else
|
|
803
|
-
this.value = this.options.default || void 0;
|
|
228
|
+
table(name) {
|
|
229
|
+
return new Table(this, name.toString());
|
|
804
230
|
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
*/
|
|
811
|
-
watch(fn2) {
|
|
812
|
-
const index = Object.keys(this.watches).length;
|
|
813
|
-
this.watches[index] = fn2;
|
|
814
|
-
return () => {
|
|
815
|
-
delete this.watches[index];
|
|
816
|
-
};
|
|
231
|
+
}
|
|
232
|
+
class Table {
|
|
233
|
+
constructor(database, name) {
|
|
234
|
+
this.database = database;
|
|
235
|
+
this.name = name;
|
|
817
236
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
237
|
+
async tx(operation, readonly = false) {
|
|
238
|
+
const db = await this.database.connection;
|
|
239
|
+
return new Promise((resolve, reject) => {
|
|
240
|
+
const request = operation(db.transaction(this.name, readonly ? "readonly" : "readwrite").objectStore(this.name));
|
|
241
|
+
request.onsuccess = () => resolve(request.result);
|
|
242
|
+
request.onerror = () => reject(request.error);
|
|
243
|
+
});
|
|
825
244
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
*
|
|
829
|
-
* @returns {T} Current value
|
|
830
|
-
*/
|
|
831
|
-
valueOf() {
|
|
832
|
-
return this.value;
|
|
245
|
+
add(value, key) {
|
|
246
|
+
return this.tx((store) => store.add(value, key));
|
|
833
247
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
function persist(options) {
|
|
837
|
-
return (target, prop) => {
|
|
838
|
-
const key = (options == null ? void 0 : options.key) || `${target.constructor.name}.${prop.toString()}`;
|
|
839
|
-
const wrapper = new Persist(key, options);
|
|
840
|
-
Object.defineProperty(target, prop, {
|
|
841
|
-
get: function() {
|
|
842
|
-
return wrapper.value;
|
|
843
|
-
},
|
|
844
|
-
set: function(v) {
|
|
845
|
-
wrapper.value = v;
|
|
846
|
-
}
|
|
847
|
-
});
|
|
848
|
-
};
|
|
849
|
-
}
|
|
850
|
-
persist$1.persist = persist;
|
|
851
|
-
var memoryStorage = {};
|
|
852
|
-
Object.defineProperty(memoryStorage, "__esModule", { value: true });
|
|
853
|
-
memoryStorage.MemoryStorage = void 0;
|
|
854
|
-
class MemoryStorage {
|
|
855
|
-
get length() {
|
|
856
|
-
return Object.keys(this).length;
|
|
248
|
+
put(key, value) {
|
|
249
|
+
return this.tx((store) => store.put(value, key));
|
|
857
250
|
}
|
|
858
|
-
|
|
859
|
-
|
|
251
|
+
get(key) {
|
|
252
|
+
return this.tx((store) => store.get(key), true);
|
|
860
253
|
}
|
|
861
|
-
|
|
862
|
-
return this
|
|
254
|
+
getAll() {
|
|
255
|
+
return this.tx((store) => store.getAll(), true);
|
|
863
256
|
}
|
|
864
|
-
|
|
865
|
-
return
|
|
257
|
+
getAllKeys() {
|
|
258
|
+
return this.tx((store) => store.getAllKeys(), true);
|
|
259
|
+
}
|
|
260
|
+
delete(key) {
|
|
261
|
+
return this.tx((store) => store.delete(key));
|
|
866
262
|
}
|
|
867
|
-
|
|
868
|
-
|
|
263
|
+
clear() {
|
|
264
|
+
return this.tx((store) => store.clear());
|
|
869
265
|
}
|
|
870
|
-
|
|
871
|
-
this
|
|
266
|
+
count() {
|
|
267
|
+
return this.tx((store) => store.count(), true);
|
|
872
268
|
}
|
|
873
269
|
}
|
|
874
|
-
memoryStorage.MemoryStorage = MemoryStorage;
|
|
875
|
-
(function(exports) {
|
|
876
|
-
var __createBinding = commonjsGlobal && commonjsGlobal.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
877
|
-
if (k2 === void 0) k2 = k;
|
|
878
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
879
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
880
|
-
desc = { enumerable: true, get: function() {
|
|
881
|
-
return m[k];
|
|
882
|
-
} };
|
|
883
|
-
}
|
|
884
|
-
Object.defineProperty(o, k2, desc);
|
|
885
|
-
} : function(o, m, k, k2) {
|
|
886
|
-
if (k2 === void 0) k2 = k;
|
|
887
|
-
o[k2] = m[k];
|
|
888
|
-
});
|
|
889
|
-
var __exportStar = commonjsGlobal && commonjsGlobal.__exportStar || function(m, exports2) {
|
|
890
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p);
|
|
891
|
-
};
|
|
892
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
893
|
-
__exportStar(persist$1, exports);
|
|
894
|
-
__exportStar(memoryStorage, exports);
|
|
895
|
-
})(dist);
|
|
896
270
|
var extendStatics = function(d, b) {
|
|
897
271
|
extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) {
|
|
898
272
|
d2.__proto__ = b2;
|
|
@@ -1747,6 +1121,7 @@ const _LoginPrompt = class _LoginPrompt {
|
|
|
1747
1121
|
__publicField(this, "alert");
|
|
1748
1122
|
__publicField(this, "button");
|
|
1749
1123
|
__publicField(this, "form");
|
|
1124
|
+
__publicField(this, "forgotLink");
|
|
1750
1125
|
__publicField(this, "password");
|
|
1751
1126
|
__publicField(this, "persist");
|
|
1752
1127
|
__publicField(this, "username");
|
|
@@ -1775,8 +1150,14 @@ const _LoginPrompt = class _LoginPrompt {
|
|
|
1775
1150
|
this.password = document.querySelector('#datalynk-login-form input[name="password"]');
|
|
1776
1151
|
this.persist = document.querySelector('#datalynk-login-form input[name="persist"]');
|
|
1777
1152
|
this.username = document.querySelector('#datalynk-login-form input[name="username"]');
|
|
1153
|
+
this.forgotLink = document.querySelector("#datalynk-login-forgot");
|
|
1778
1154
|
if (this.options.persist === false) this.persist.parentElement.remove();
|
|
1779
1155
|
this.form.onsubmit = (event) => this.login(event);
|
|
1156
|
+
this.forgotLink.onclick = (event) => this.forgotPassword(event);
|
|
1157
|
+
const toggleForgotVisibility = () => this.forgotLink.classList.toggle("hidden", !this.username.value.trim());
|
|
1158
|
+
this.username.addEventListener("input", toggleForgotVisibility);
|
|
1159
|
+
const passwordToggle = document.querySelector("#datalynk-password-toggle");
|
|
1160
|
+
if (passwordToggle) passwordToggle.onclick = () => this.password.type = this.password.type === "password" ? "text" : "password";
|
|
1780
1161
|
const pwaLink = document.querySelector("#pwa-install-link");
|
|
1781
1162
|
if (pwaLink) {
|
|
1782
1163
|
pwaLink.addEventListener("click", async (e) => {
|
|
@@ -1848,6 +1229,19 @@ const _LoginPrompt = class _LoginPrompt {
|
|
|
1848
1229
|
this.button.disabled = false;
|
|
1849
1230
|
});
|
|
1850
1231
|
}
|
|
1232
|
+
/** Forgot password link click event */
|
|
1233
|
+
forgotPassword(event) {
|
|
1234
|
+
event.preventDefault();
|
|
1235
|
+
const login = this.username.value.trim() || prompt("Enter your email or username") || "";
|
|
1236
|
+
if (!login) return;
|
|
1237
|
+
this.alert.classList.remove("hidden");
|
|
1238
|
+
this.alert.innerHTML = "Sending reset email...";
|
|
1239
|
+
return this.api.auth.resetRequest(login, "email").then(() => {
|
|
1240
|
+
this.alert.innerHTML = "If an account exists, a reset email has been sent.";
|
|
1241
|
+
}).catch((err) => {
|
|
1242
|
+
this.alert.innerHTML = err.message || "Unable to send reset email.";
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1851
1245
|
};
|
|
1852
1246
|
/** Dynamically create CSS style */
|
|
1853
1247
|
__publicField(_LoginPrompt, "css", (options) => `
|
|
@@ -2000,6 +1394,35 @@ __publicField(_LoginPrompt, "css", (options) => `
|
|
|
2000
1394
|
text-decoration: none;
|
|
2001
1395
|
}
|
|
2002
1396
|
|
|
1397
|
+
#datalynk-login .login-forgot {
|
|
1398
|
+
display: block;
|
|
1399
|
+
text-align: left;
|
|
1400
|
+
margin-bottom: 0.75rem;
|
|
1401
|
+
color: var(--theme-text);
|
|
1402
|
+
font-size: 14px;
|
|
1403
|
+
text-decoration: none;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
#datalynk-login .login-forgot:hover {
|
|
1407
|
+
text-decoration: underline;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
#datalynk-login .password-wrapper {
|
|
1411
|
+
position: relative;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
#datalynk-login .password-toggle {
|
|
1415
|
+
position: absolute;
|
|
1416
|
+
right: 30px;
|
|
1417
|
+
top: 50%;
|
|
1418
|
+
transform: translateY(-50%);
|
|
1419
|
+
background: none;
|
|
1420
|
+
border: none;
|
|
1421
|
+
padding: 0;
|
|
1422
|
+
color: #333;
|
|
1423
|
+
cursor: pointer;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
2003
1426
|
#datalynk-login #pwa-install-link {
|
|
2004
1427
|
display: inline-flex;
|
|
2005
1428
|
align-items: center;
|
|
@@ -2057,7 +1480,6 @@ __publicField(_LoginPrompt, "template", (options) => {
|
|
|
2057
1480
|
</div>
|
|
2058
1481
|
<div class="login-content">
|
|
2059
1482
|
<div class="login-body" style="max-width: 300px">
|
|
2060
|
-
<div id="datalynk-login-alert" class="hidden"></div>
|
|
2061
1483
|
<form id="datalynk-login-form">
|
|
2062
1484
|
<div>
|
|
2063
1485
|
<label for="username">Email or Username</label>
|
|
@@ -2066,12 +1488,19 @@ __publicField(_LoginPrompt, "template", (options) => {
|
|
|
2066
1488
|
<br>
|
|
2067
1489
|
<div>
|
|
2068
1490
|
<label for="password">Password</label>
|
|
2069
|
-
<
|
|
1491
|
+
<div class="password-wrapper">
|
|
1492
|
+
<input id="password" name="password" type="password" autocomplete="current-password">
|
|
1493
|
+
<button type="button" id="datalynk-password-toggle" class="password-toggle" tabindex="-1">
|
|
1494
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px" fill="currentColor"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zm0 12.5c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5zm0-8a3 3 0 100 6 3 3 0 000-6z"/></svg>
|
|
1495
|
+
</button>
|
|
1496
|
+
</div>
|
|
2070
1497
|
</div>
|
|
1498
|
+
<a href="#" id="datalynk-login-forgot" class="login-forgot hidden">Forgot Password?</a>
|
|
2071
1499
|
<br>
|
|
2072
1500
|
<label style="display: block; margin-bottom: 0.75rem;">
|
|
2073
1501
|
<input type="checkbox" name="persist" style="width: 20px"> Stay Logged In
|
|
2074
1502
|
</label>
|
|
1503
|
+
<div id="datalynk-login-alert" class="hidden"></div>
|
|
2075
1504
|
<button type="submit">Login</button>
|
|
2076
1505
|
</form>
|
|
2077
1506
|
</div>
|
|
@@ -2112,7 +1541,12 @@ class Auth {
|
|
|
2112
1541
|
this.api = api;
|
|
2113
1542
|
this.api.token$.subscribe(async (token) => {
|
|
2114
1543
|
if (token === void 0) return;
|
|
2115
|
-
|
|
1544
|
+
try {
|
|
1545
|
+
this.user = await this.current(token);
|
|
1546
|
+
} catch (error) {
|
|
1547
|
+
if (this.api.status === "unauthorized") this.user = null;
|
|
1548
|
+
else console.error("Unable to refresh the current Datalynk user", error);
|
|
1549
|
+
}
|
|
2116
1550
|
});
|
|
2117
1551
|
if ((_a = this.api.options.offline) == null ? void 0 : _a.length)
|
|
2118
1552
|
this.user$.pipe(filter((u) => u !== void 0)).subscribe((u) => localStorage.setItem("datalynk-user", JSON.stringify(u)));
|
|
@@ -2632,9 +2066,9 @@ class PWA {
|
|
|
2632
2066
|
document.head.append(style);
|
|
2633
2067
|
}
|
|
2634
2068
|
const iconUrl = this.resolvedIconUrl || "https://datalynk-client.primary.auxilium.world/logo.png";
|
|
2635
|
-
const
|
|
2636
|
-
|
|
2637
|
-
|
|
2069
|
+
const prompt2 = document.createElement("div");
|
|
2070
|
+
prompt2.classList.add("pwa-prompt");
|
|
2071
|
+
prompt2.innerHTML = `
|
|
2638
2072
|
<div class="pwa-prompt-header">
|
|
2639
2073
|
<img src="${iconUrl}" alt="Logo" />
|
|
2640
2074
|
<h1>Install ${this.api.options.name}</h1>
|
|
@@ -2650,14 +2084,14 @@ class PWA {
|
|
|
2650
2084
|
${this.nativeInstallPrompt ? `<button id="installPwaBtn">Install App</button>` : stepsHtml}
|
|
2651
2085
|
</div>
|
|
2652
2086
|
`;
|
|
2653
|
-
const closeBtn =
|
|
2087
|
+
const closeBtn = prompt2.querySelector(".pwa-prompt-close");
|
|
2654
2088
|
closeBtn.onclick = () => {
|
|
2655
2089
|
var _a2;
|
|
2656
2090
|
!!((_a2 = this.api.options.pwaSettings) == null ? void 0 : _a2.dismissExpiry) ? localStorage.setItem(storageKey, Date.now().toString()) : localStorage.removeItem(storageKey);
|
|
2657
|
-
|
|
2091
|
+
prompt2.remove();
|
|
2658
2092
|
};
|
|
2659
|
-
document.body.append(
|
|
2660
|
-
const img =
|
|
2093
|
+
document.body.append(prompt2);
|
|
2094
|
+
const img = prompt2.querySelector(".pwa-prompt-header img");
|
|
2661
2095
|
if (img) img.onerror = () => img.src = "https://datalynk-client.primary.auxilium.world/logo.png";
|
|
2662
2096
|
if (this.nativeInstallPrompt) this.bindInstallButton();
|
|
2663
2097
|
}
|
|
@@ -3245,7 +2679,13 @@ const _Slice = class _Slice {
|
|
|
3245
2679
|
set cache(cache) {
|
|
3246
2680
|
this.cache$.next(cache);
|
|
3247
2681
|
}
|
|
3248
|
-
/**
|
|
2682
|
+
/**
|
|
2683
|
+
* Whether this slice has local IndexedDB/offline support enabled.
|
|
2684
|
+
*
|
|
2685
|
+
* Offline-enabled slices can fall back to their local cache when either the
|
|
2686
|
+
* browser loses network connectivity or the Datalynk client enters an
|
|
2687
|
+
* unavailable recovery state after a returned server/API failure.
|
|
2688
|
+
*/
|
|
3249
2689
|
get offlineEnabled() {
|
|
3250
2690
|
var _a;
|
|
3251
2691
|
return (_a = this.api.database) == null ? void 0 : _a.includes(this.slice.toString());
|
|
@@ -3258,7 +2698,7 @@ const _Slice = class _Slice {
|
|
|
3258
2698
|
const onlineExec = call.exec.bind(call);
|
|
3259
2699
|
return async () => {
|
|
3260
2700
|
const offlineSupported = this.offlineEnabled && typeof navigator !== "undefined";
|
|
3261
|
-
const offlineNow = offlineSupported && !(navigator == null ? void 0 : navigator.onLine);
|
|
2701
|
+
const offlineNow = offlineSupported && (!this.api.online || !(navigator == null ? void 0 : navigator.onLine));
|
|
3262
2702
|
const offlineSim = async () => {
|
|
3263
2703
|
var _a, _b, _c;
|
|
3264
2704
|
const where = (row, condition) => {
|
|
@@ -3557,24 +2997,39 @@ const _Slice = class _Slice {
|
|
|
3557
2997
|
return this.info;
|
|
3558
2998
|
}
|
|
3559
2999
|
/**
|
|
3560
|
-
* Synchronize cache with server
|
|
3000
|
+
* Synchronize the local slice cache with the server and subscribe to socket changes.
|
|
3001
|
+
*
|
|
3002
|
+
* For an offline-enabled slice, synchronization failures caused by network or
|
|
3003
|
+
* API unavailability are contained in the background rather than becoming
|
|
3004
|
+
* unhandled promise rejections. Local cached data remains usable while the
|
|
3005
|
+
* {@link Api} recovery loop determines when the failed API path works again.
|
|
3006
|
+
* When the client becomes online again, pending local changes are scheduled
|
|
3007
|
+
* for upload without overlapping push operations.
|
|
3008
|
+
*
|
|
3009
|
+
* Calling `sync(false)` unsubscribes from slice socket events.
|
|
3010
|
+
*
|
|
3561
3011
|
* @example
|
|
3562
3012
|
* ```ts
|
|
3563
3013
|
* const slice: Slice = new Slice<T>(Slices.Contact);
|
|
3564
|
-
* slice.sync()
|
|
3014
|
+
* const rows$ = slice.sync();
|
|
3015
|
+
* rows$?.subscribe((rows: T[]) => console.log(rows));
|
|
3565
3016
|
* ```
|
|
3566
|
-
* @param
|
|
3567
|
-
* @
|
|
3017
|
+
* @param on Enable or disable synchronization/socket events.
|
|
3018
|
+
* @returns The observable local cache when synchronization is enabled.
|
|
3568
3019
|
*/
|
|
3569
3020
|
sync(on = true) {
|
|
3570
3021
|
if (on) {
|
|
3571
|
-
this.pushChanges().then(() => this.select().rows().exec().then((rows) => {
|
|
3022
|
+
void this.pushChanges().then(() => this.select().rows().exec()).then((rows) => {
|
|
3572
3023
|
this.cache = rows;
|
|
3573
3024
|
this.loaded = true;
|
|
3574
|
-
}))
|
|
3025
|
+
}).catch((error) => {
|
|
3026
|
+
if (this.api.online) console.warn("Unable to synchronize offline slice", error);
|
|
3027
|
+
});
|
|
3575
3028
|
if (!this.unsubscribe) this.unsubscribe = this.api.socket.sliceEvents(this.slice, (event) => {
|
|
3576
3029
|
const ids = [...event.data.new, ...event.data.changed];
|
|
3577
|
-
this.select(ids).rows().exec().then((rows) => this.cache = [...this.cache.filter((c) => c.id != null && !ids.includes(c.id)), ...rows])
|
|
3030
|
+
void this.select(ids).rows().exec().then((rows) => this.cache = [...this.cache.filter((c) => c.id != null && !ids.includes(c.id)), ...rows]).catch((error) => {
|
|
3031
|
+
if (this.api.online) console.warn("Unable to refresh offline slice from socket event", error);
|
|
3032
|
+
});
|
|
3578
3033
|
this.cache = this.cache.filter((v) => v.id && !event.data.lost.includes(v.id));
|
|
3579
3034
|
});
|
|
3580
3035
|
return this.cache$;
|
|
@@ -3647,7 +3102,7 @@ class Socket {
|
|
|
3647
3102
|
this.options = options;
|
|
3648
3103
|
if (!options.url && options.url !== false) {
|
|
3649
3104
|
const origin = new URL(this.api.url).origin;
|
|
3650
|
-
this.options.url = origin.replace("http", "ws").replace(/:\d+/g, "") +
|
|
3105
|
+
this.options.url = origin.replace("http", "ws").replace(/:\d+/g, "") + `/s/`;
|
|
3651
3106
|
}
|
|
3652
3107
|
if (this.options.url !== false)
|
|
3653
3108
|
api.token$.pipe(filter((u) => u !== void 0), distinctUntilChanged()).subscribe(() => this.connect());
|
|
@@ -3770,7 +3225,7 @@ class Superuser {
|
|
|
3770
3225
|
} });
|
|
3771
3226
|
}
|
|
3772
3227
|
}
|
|
3773
|
-
const version = "1.
|
|
3228
|
+
const version = "1.5.0";
|
|
3774
3229
|
class WebRtc {
|
|
3775
3230
|
constructor(api) {
|
|
3776
3231
|
__publicField(this, "ice");
|
|
@@ -3891,8 +3346,9 @@ class WebRtc {
|
|
|
3891
3346
|
return session;
|
|
3892
3347
|
}
|
|
3893
3348
|
}
|
|
3894
|
-
class
|
|
3349
|
+
const _Gps = class _Gps {
|
|
3895
3350
|
constructor(api, options) {
|
|
3351
|
+
__publicField(this, "ackResolvers", /* @__PURE__ */ new Map());
|
|
3896
3352
|
__publicField(this, "deviceId");
|
|
3897
3353
|
__publicField(this, "heartbeat");
|
|
3898
3354
|
__publicField(this, "lastFixAt", 0);
|
|
@@ -3910,6 +3366,15 @@ class Gps {
|
|
|
3910
3366
|
__publicField(this, "watchId");
|
|
3911
3367
|
__publicField(this, "watchStartedAt", 0);
|
|
3912
3368
|
__publicField(this, "options");
|
|
3369
|
+
__publicField(this, "onSocketMessage", (event) => {
|
|
3370
|
+
const ack = event == null ? void 0 : event.gps;
|
|
3371
|
+
if (!ack || ack.seq == null) return;
|
|
3372
|
+
const pending = this.ackResolvers.get(Number(ack.seq));
|
|
3373
|
+
if (!pending) return;
|
|
3374
|
+
clearTimeout(pending.timeout);
|
|
3375
|
+
this.ackResolvers.delete(Number(ack.seq));
|
|
3376
|
+
pending.resolve(ack);
|
|
3377
|
+
});
|
|
3913
3378
|
__publicField(this, "onPosition", (position) => {
|
|
3914
3379
|
if (!this.options) return;
|
|
3915
3380
|
if (!this.isFresh(position)) return;
|
|
@@ -3965,18 +3430,23 @@ class Gps {
|
|
|
3965
3430
|
this.unsubs.forEach((unsub) => unsub());
|
|
3966
3431
|
this.unsubs = [];
|
|
3967
3432
|
this.pendingPayloads = [];
|
|
3433
|
+
this.clearAckResolvers("GPS stopped");
|
|
3968
3434
|
(_a = this.socket) == null ? void 0 : _a.close();
|
|
3969
3435
|
this.socket = void 0;
|
|
3970
3436
|
}
|
|
3971
|
-
/**
|
|
3972
|
-
|
|
3437
|
+
/**
|
|
3438
|
+
* Send a position from an app-owned GPS engine.
|
|
3439
|
+
* By default returns after the message is queued/sent; set `waitForAck` to await server confirmation.
|
|
3440
|
+
*/
|
|
3441
|
+
send(position, extra = {}, options = {}) {
|
|
3973
3442
|
if (!this.options) throw new Error("Datalynk GPS is not configured");
|
|
3974
3443
|
if (!this.api.token) throw new Error("Datalynk GPS cannot send before login");
|
|
3975
3444
|
this.connectSocket();
|
|
3976
|
-
this.
|
|
3445
|
+
const waitForAck = options.waitForAck ?? this.options.waitForAck ?? false;
|
|
3446
|
+
return this.sendPayload({
|
|
3977
3447
|
...extra,
|
|
3978
3448
|
position: this.normalizePosition(position)
|
|
3979
|
-
});
|
|
3449
|
+
}, waitForAck);
|
|
3980
3450
|
}
|
|
3981
3451
|
/** Listen to GPS updates for the configured slice/field */
|
|
3982
3452
|
listen(callback, options = {}) {
|
|
@@ -4016,12 +3486,18 @@ class Gps {
|
|
|
4016
3486
|
document.removeEventListener("visibilitychange", restart);
|
|
4017
3487
|
});
|
|
4018
3488
|
}
|
|
3489
|
+
clearAckResolvers(reason) {
|
|
3490
|
+
this.ackResolvers.forEach(({ timeout, resolve }) => {
|
|
3491
|
+
clearTimeout(timeout);
|
|
3492
|
+
resolve({ ok: false, error: reason });
|
|
3493
|
+
});
|
|
3494
|
+
this.ackResolvers.clear();
|
|
3495
|
+
}
|
|
4019
3496
|
connectSocket() {
|
|
4020
3497
|
if (!this.options || this.socket) return;
|
|
4021
3498
|
if (!this.api.token) throw new Error("Datalynk GPS socket cannot connect before login");
|
|
4022
3499
|
this.socket = new Socket(this.api, { url: this.options.socketUrl });
|
|
4023
|
-
this.unsubs.push(this.socket.addListener(() =>
|
|
4024
|
-
}, () => this.flushPendingPayloads()));
|
|
3500
|
+
this.unsubs.push(this.socket.addListener(this.onSocketMessage, () => this.flushPendingPayloads()));
|
|
4025
3501
|
}
|
|
4026
3502
|
createId() {
|
|
4027
3503
|
if (typeof crypto != "undefined" && crypto.randomUUID) return crypto.randomUUID();
|
|
@@ -4138,10 +3614,11 @@ class Gps {
|
|
|
4138
3614
|
this.watchId = void 0;
|
|
4139
3615
|
this.startWatch();
|
|
4140
3616
|
}
|
|
4141
|
-
sendPayload(payload) {
|
|
3617
|
+
sendPayload(payload, waitForAck = false) {
|
|
4142
3618
|
var _a, _b;
|
|
4143
|
-
if (!this.options) return;
|
|
3619
|
+
if (!this.options) return Promise.resolve({ ok: false, error: "Datalynk GPS is not configured" });
|
|
4144
3620
|
this.connectSocket();
|
|
3621
|
+
const seq = ++this.seq;
|
|
4145
3622
|
const message = {
|
|
4146
3623
|
gps: {
|
|
4147
3624
|
slice: this.options.slice,
|
|
@@ -4150,7 +3627,7 @@ class Gps {
|
|
|
4150
3627
|
trackerId: this.trackerId,
|
|
4151
3628
|
sessionId: this.sessionId,
|
|
4152
3629
|
deviceId: this.deviceId,
|
|
4153
|
-
seq
|
|
3630
|
+
seq,
|
|
4154
3631
|
client: this.getClientDetails(),
|
|
4155
3632
|
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4156
3633
|
gpsTimestamp: ((_a = payload.position) == null ? void 0 : _a.timestamp) || null,
|
|
@@ -4159,12 +3636,24 @@ class Gps {
|
|
|
4159
3636
|
...payload
|
|
4160
3637
|
}
|
|
4161
3638
|
};
|
|
3639
|
+
let ackPromise;
|
|
3640
|
+
if (waitForAck) {
|
|
3641
|
+
const timeoutMs = this.options.ackTimeoutMs ?? _Gps.GPS_ACK_TIMEOUT_MS;
|
|
3642
|
+
ackPromise = new Promise((resolve) => {
|
|
3643
|
+
const timeout = setTimeout(() => {
|
|
3644
|
+
this.ackResolvers.delete(seq);
|
|
3645
|
+
resolve({ ok: false, error: "GPS ack timeout", seq });
|
|
3646
|
+
}, timeoutMs);
|
|
3647
|
+
this.ackResolvers.set(seq, { resolve, timeout });
|
|
3648
|
+
});
|
|
3649
|
+
}
|
|
4162
3650
|
if (!((_b = this.socket) == null ? void 0 : _b.open)) {
|
|
4163
3651
|
this.pendingPayloads.push(message);
|
|
4164
3652
|
this.pendingPayloads = this.pendingPayloads.slice(-25);
|
|
4165
|
-
return;
|
|
3653
|
+
return ackPromise ?? Promise.resolve({ ok: true, seq, pending: true });
|
|
4166
3654
|
}
|
|
4167
3655
|
this.socket.send(message);
|
|
3656
|
+
return ackPromise ?? Promise.resolve({ ok: true, seq, pending: false });
|
|
4168
3657
|
}
|
|
4169
3658
|
sendPosition(position, force) {
|
|
4170
3659
|
this.sendPayload({
|
|
@@ -4235,7 +3724,11 @@ class Gps {
|
|
|
4235
3724
|
return;
|
|
4236
3725
|
}
|
|
4237
3726
|
const sub = this.api.token$.subscribe((token) => {
|
|
4238
|
-
if (!token
|
|
3727
|
+
if (!token) {
|
|
3728
|
+
if (this.started) this.stop();
|
|
3729
|
+
return;
|
|
3730
|
+
}
|
|
3731
|
+
if (this.started || !this.options || this.options.autoStart === false) return;
|
|
4239
3732
|
try {
|
|
4240
3733
|
this.start();
|
|
4241
3734
|
} catch (error) {
|
|
@@ -4244,6 +3737,16 @@ class Gps {
|
|
|
4244
3737
|
});
|
|
4245
3738
|
this.unsubs.push(() => sub.unsubscribe());
|
|
4246
3739
|
}
|
|
3740
|
+
};
|
|
3741
|
+
__publicField(_Gps, "GPS_ACK_TIMEOUT_MS", 15e3);
|
|
3742
|
+
let Gps = _Gps;
|
|
3743
|
+
class UnexpectedApiResponseError extends Error {
|
|
3744
|
+
constructor(response, body) {
|
|
3745
|
+
super(`Unexpected API response (${response.status} ${response.statusText})`);
|
|
3746
|
+
this.response = response;
|
|
3747
|
+
this.body = body;
|
|
3748
|
+
this.name = "UnexpectedApiResponseError";
|
|
3749
|
+
}
|
|
4247
3750
|
}
|
|
4248
3751
|
const _Api = class _Api {
|
|
4249
3752
|
/**
|
|
@@ -4268,6 +3771,13 @@ const _Api = class _Api {
|
|
|
4268
3771
|
target: "version.php",
|
|
4269
3772
|
timeout: 6e4
|
|
4270
3773
|
});
|
|
3774
|
+
__publicField(this, "authenticationInvalid", false);
|
|
3775
|
+
__publicField(this, "tokenExpiryTimeout", null);
|
|
3776
|
+
/** Request-specific recovery after a server response proves an API call is broken. */
|
|
3777
|
+
__publicField(this, "recovery", null);
|
|
3778
|
+
/** Retry the failed request twice immediately, then this long after each failed recovery response. */
|
|
3779
|
+
__publicField(this, "recoveryRetryInterval", 3e4);
|
|
3780
|
+
__publicField(this, "recoveryImmediateRetries", 2);
|
|
4271
3781
|
/** LocalStorage key for persisting logins */
|
|
4272
3782
|
__publicField(this, "localStorageKey", "datalynk-token");
|
|
4273
3783
|
/** Pending requests cache */
|
|
@@ -4300,7 +3810,20 @@ const _Api = class _Api {
|
|
|
4300
3810
|
/** Client library version */
|
|
4301
3811
|
__publicField(this, "version", version);
|
|
4302
3812
|
__publicField(this, "onlineOverride", false);
|
|
4303
|
-
__publicField(this, "
|
|
3813
|
+
__publicField(this, "initialOnline", typeof navigator == "undefined" || typeof navigator.onLine == "undefined" ? true : navigator.onLine);
|
|
3814
|
+
/**
|
|
3815
|
+
* Detailed API connection state.
|
|
3816
|
+
*
|
|
3817
|
+
* Subscribe to this when callers need to distinguish physical/network
|
|
3818
|
+
* offline state from authentication failure or server/API unavailability.
|
|
3819
|
+
*/
|
|
3820
|
+
__publicField(this, "status$", new BehaviorSubject(this.initialOnline ? "online" : "offline"));
|
|
3821
|
+
/**
|
|
3822
|
+
* Backwards-compatible boolean connection state.
|
|
3823
|
+
*
|
|
3824
|
+
* `false` includes `offline`, `unauthorized`, and `unavailable` states.
|
|
3825
|
+
*/
|
|
3826
|
+
__publicField(this, "online$", new BehaviorSubject(this.initialOnline));
|
|
4304
3827
|
/** API Session token */
|
|
4305
3828
|
__publicField(this, "token$", new BehaviorSubject(void 0));
|
|
4306
3829
|
var _a, _b;
|
|
@@ -4321,14 +3844,14 @@ const _Api = class _Api {
|
|
|
4321
3844
|
...options.webrtc || {}
|
|
4322
3845
|
}
|
|
4323
3846
|
};
|
|
4324
|
-
if (this.options.saveSession) {
|
|
4325
|
-
if (typeof localStorage == "undefined") return;
|
|
3847
|
+
if (this.options.saveSession && typeof localStorage != "undefined") {
|
|
4326
3848
|
this.token = localStorage.getItem(this.localStorageKey) || null;
|
|
4327
3849
|
this.token$.pipe(distinctUntilChanged()).subscribe((token) => {
|
|
4328
3850
|
if (token) localStorage.setItem(this.localStorageKey, token);
|
|
4329
3851
|
else localStorage.removeItem(this.localStorageKey);
|
|
4330
3852
|
});
|
|
4331
3853
|
}
|
|
3854
|
+
this.token$.pipe(distinctUntilChanged()).subscribe((token) => this.scheduleTokenExpiry(token));
|
|
4332
3855
|
this.socket = new Socket(this, { url: options.socket });
|
|
4333
3856
|
this.gps = new Gps(this, options.gps);
|
|
4334
3857
|
this.auth = new Auth(this);
|
|
@@ -4351,12 +3874,10 @@ const _Api = class _Api {
|
|
|
4351
3874
|
});
|
|
4352
3875
|
}
|
|
4353
3876
|
if (typeof window !== "undefined") {
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
};
|
|
4357
|
-
window.addEventListener("online", () => handleOffline(true));
|
|
4358
|
-
window.addEventListener("offline", () => handleOffline(false));
|
|
3877
|
+
window.addEventListener("online", () => this.checkConnection());
|
|
3878
|
+
window.addEventListener("offline", () => this.setConnectionStatus("offline"));
|
|
4359
3879
|
this.online$.subscribe(() => this.offlineBanner());
|
|
3880
|
+
this.startHeartbeat();
|
|
4360
3881
|
}
|
|
4361
3882
|
if ((_a = this.options.offline) == null ? void 0 : _a.length) {
|
|
4362
3883
|
this.pwa.setup();
|
|
@@ -4389,26 +3910,50 @@ const _Api = class _Api {
|
|
|
4389
3910
|
/** Get session info from JWT payload */
|
|
4390
3911
|
get jwtPayload() {
|
|
4391
3912
|
if (!this.token) return null;
|
|
4392
|
-
|
|
3913
|
+
try {
|
|
3914
|
+
return decodeJwt(this.token);
|
|
3915
|
+
} catch {
|
|
3916
|
+
return null;
|
|
3917
|
+
}
|
|
4393
3918
|
}
|
|
4394
|
-
/**
|
|
3919
|
+
/** Current detailed Datalynk API connection state. */
|
|
3920
|
+
get status() {
|
|
3921
|
+
return this.status$.getValue();
|
|
3922
|
+
}
|
|
3923
|
+
/**
|
|
3924
|
+
* Whether normal Datalynk network requests are currently available.
|
|
3925
|
+
*
|
|
3926
|
+
* This becomes `false` for network outages, rejected/expired sessions, and
|
|
3927
|
+
* request-owned server recovery. It therefore describes Datalynk
|
|
3928
|
+
* availability rather than only `navigator.onLine`.
|
|
3929
|
+
*/
|
|
4395
3930
|
get online() {
|
|
4396
3931
|
return this.online$.getValue();
|
|
4397
3932
|
}
|
|
3933
|
+
/**
|
|
3934
|
+
* Whether normal Datalynk API access is currently unavailable.
|
|
3935
|
+
*
|
|
3936
|
+
* This is the inverse of {@link online}. It can be `true` while the browser
|
|
3937
|
+
* still has Internet access, for example during MySQL/HTTP 5xx recovery.
|
|
3938
|
+
*/
|
|
4398
3939
|
get offline() {
|
|
4399
3940
|
return !this.online;
|
|
4400
3941
|
}
|
|
4401
|
-
/**
|
|
3942
|
+
/**
|
|
3943
|
+
* Override the boolean connection state.
|
|
3944
|
+
*
|
|
3945
|
+
* Set `true` or `false` to force the corresponding state. Set `null` to
|
|
3946
|
+
* remove the override and resume normal connection checking. This is a
|
|
3947
|
+
* manual override and can supersede the current recovery state, so normal
|
|
3948
|
+
* applications should generally observe {@link status} instead of forcing it.
|
|
3949
|
+
*/
|
|
4402
3950
|
set online(value) {
|
|
4403
3951
|
if (value == null) {
|
|
4404
3952
|
this.onlineOverride = false;
|
|
4405
|
-
this.
|
|
3953
|
+
this.checkConnection();
|
|
4406
3954
|
} else {
|
|
4407
3955
|
this.onlineOverride = true;
|
|
4408
|
-
|
|
4409
|
-
this.online$.next(value);
|
|
4410
|
-
if (value) this.startHeartbeat();
|
|
4411
|
-
else this.stopHeartbeat();
|
|
3956
|
+
this.setConnectionStatus(value ? "online" : "offline");
|
|
4412
3957
|
}
|
|
4413
3958
|
}
|
|
4414
3959
|
/** Logged in spoke */
|
|
@@ -4420,56 +3965,246 @@ const _Api = class _Api {
|
|
|
4420
3965
|
return this.token$.getValue();
|
|
4421
3966
|
}
|
|
4422
3967
|
set token(token) {
|
|
3968
|
+
if (this.recovery && token !== this.recovery.token)
|
|
3969
|
+
this.cancelRecovery(errorFromCode(401, "Session changed during API recovery"));
|
|
3970
|
+
if (token && this.isTokenExpired(token)) {
|
|
3971
|
+
this.authenticationInvalid = true;
|
|
3972
|
+
this.token$.next(null);
|
|
3973
|
+
this.setConnectionStatus("unauthorized");
|
|
3974
|
+
return;
|
|
3975
|
+
}
|
|
3976
|
+
this.authenticationInvalid = false;
|
|
4423
3977
|
this.token$.next(token);
|
|
3978
|
+
if (token && !this.online && !this.recovery) {
|
|
3979
|
+
this.setConnectionStatus(typeof navigator == "undefined" || navigator.onLine ? "online" : "offline");
|
|
3980
|
+
}
|
|
4424
3981
|
}
|
|
4425
|
-
_request(req, options = {}) {
|
|
3982
|
+
async _request(req, options = {}) {
|
|
3983
|
+
if (this.recovery) throw errorFromCode(503, "Datalynk is unavailable");
|
|
3984
|
+
const retryRequest = deepCopy(req);
|
|
3985
|
+
const retryOptions = { ...options };
|
|
4426
3986
|
const token = options.token || this.token;
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
3987
|
+
try {
|
|
3988
|
+
return await this._requestOnce(req, options, false);
|
|
3989
|
+
} catch (error) {
|
|
3990
|
+
if (!this.isRecoverableResponseError(error)) throw error;
|
|
3991
|
+
this.beginRecovery(retryRequest, retryOptions, token);
|
|
3992
|
+
throw error;
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
/** Execute exactly one HTTP API attempt. Recovery uses this directly to avoid recursive retry loops. */
|
|
3996
|
+
async _requestOnce(req, options = {}, recoveryAttempt = false) {
|
|
3997
|
+
const token = options.token || this.token;
|
|
3998
|
+
if (token && this.isTokenExpired(token)) {
|
|
3999
|
+
this.markUnauthorized(token);
|
|
4000
|
+
throw errorFromCode(401, "Session token expired");
|
|
4001
|
+
}
|
|
4002
|
+
let resp;
|
|
4003
|
+
try {
|
|
4004
|
+
resp = await fetch(this.url, {
|
|
4005
|
+
method: "POST",
|
|
4006
|
+
headers: clean({
|
|
4007
|
+
Authorization: token ? `Bearer ${token}` : void 0,
|
|
4008
|
+
"Content-Type": "application/json",
|
|
4009
|
+
"X-Date-Return-Format": this.options.legacyDates ? void 0 : "ISO8601"
|
|
4010
|
+
}),
|
|
4011
|
+
body: JSON.stringify(_Api.translateTokens(req))
|
|
4012
|
+
});
|
|
4013
|
+
} catch (error) {
|
|
4014
|
+
this.setConnectionStatus("offline");
|
|
4015
|
+
throw error;
|
|
4016
|
+
}
|
|
4017
|
+
const warning = resp.headers.get("X-Warning");
|
|
4018
|
+
if (warning) console.warn(warning);
|
|
4019
|
+
const banner = resp.headers.get("X-User-Notice");
|
|
4020
|
+
if (banner) {
|
|
4021
|
+
createBanner(banner);
|
|
4022
|
+
setTimeout(() => removeBanner(), 1e4);
|
|
4023
|
+
}
|
|
4024
|
+
const body = await resp.text();
|
|
4025
|
+
let data;
|
|
4026
|
+
try {
|
|
4027
|
+
data = JSON.parse(body);
|
|
4028
|
+
} catch {
|
|
4029
|
+
throw new UnexpectedApiResponseError(resp, body);
|
|
4030
|
+
}
|
|
4031
|
+
if (resp.status === 401) this.markUnauthorized(token);
|
|
4032
|
+
if (!resp.ok || (data == null ? void 0 : data.error)) {
|
|
4033
|
+
const error = Object.assign(errorFromCode(resp.status, data == null ? void 0 : data.error), data);
|
|
4034
|
+
Object.defineProperty(error, "response", { value: resp, configurable: true });
|
|
4035
|
+
if (!recoveryAttempt && resp.status < 500 && resp.status !== 401 && !this.isMysqlError(error))
|
|
4036
|
+
this.setConnectionStatus("online");
|
|
4037
|
+
throw error;
|
|
4038
|
+
}
|
|
4039
|
+
if (!options.raw) data = _Api.translateTokens(data);
|
|
4040
|
+
if (!recoveryAttempt) this.setConnectionStatus("online");
|
|
4041
|
+
return data;
|
|
4042
|
+
}
|
|
4043
|
+
/** Only server-response failures own global recovery; auth and ordinary 4xx errors do not. */
|
|
4044
|
+
isRecoverableResponseError(error) {
|
|
4045
|
+
var _a;
|
|
4046
|
+
if (error instanceof UnexpectedApiResponseError) return true;
|
|
4047
|
+
if ((error == null ? void 0 : error.code) === 401) return false;
|
|
4048
|
+
const status = ((_a = error == null ? void 0 : error.response) == null ? void 0 : _a.status) ?? (error == null ? void 0 : error.code);
|
|
4049
|
+
return status >= 500 || this.isMysqlError(error);
|
|
4050
|
+
}
|
|
4051
|
+
isMysqlError(error) {
|
|
4052
|
+
const message = String((error == null ? void 0 : error.error) ?? (error == null ? void 0 : error.message) ?? "");
|
|
4053
|
+
return /\bSQLSTATE\[[A-Z0-9]+\]/i.test(message);
|
|
4054
|
+
}
|
|
4055
|
+
/**
|
|
4056
|
+
* A returned server failure immediately makes the client unavailable. The exact
|
|
4057
|
+
* failed request is then retried twice back-to-back. After that, retries are
|
|
4058
|
+
* scheduled 30 seconds after each completed failed attempt, never with overlap.
|
|
4059
|
+
*/
|
|
4060
|
+
beginRecovery(req, options, token) {
|
|
4061
|
+
if (this.recovery) return;
|
|
4062
|
+
this.setConnectionStatus("unavailable");
|
|
4063
|
+
this.recovery = {
|
|
4064
|
+
request: deepCopy(req),
|
|
4065
|
+
options: { ...options },
|
|
4066
|
+
token,
|
|
4067
|
+
timer: null,
|
|
4068
|
+
running: false
|
|
4069
|
+
};
|
|
4070
|
+
void this.runImmediateRecovery();
|
|
4071
|
+
}
|
|
4072
|
+
async runImmediateRecovery() {
|
|
4073
|
+
for (let attempt = 0; attempt < this.recoveryImmediateRetries; attempt++) {
|
|
4074
|
+
const result = await this.tryRecoveryOnce();
|
|
4075
|
+
if (result.ok) {
|
|
4076
|
+
this.finishRecovery();
|
|
4077
|
+
return;
|
|
4442
4078
|
}
|
|
4443
|
-
this.
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4079
|
+
if (!this.recovery || this.status === "unauthorized") return;
|
|
4080
|
+
}
|
|
4081
|
+
if (this.recovery) this.scheduleRecovery();
|
|
4082
|
+
}
|
|
4083
|
+
async tryRecoveryOnce() {
|
|
4084
|
+
const recovery = this.recovery;
|
|
4085
|
+
if (!recovery) return { ok: false, error: errorFromCode(503, "API recovery was cancelled") };
|
|
4086
|
+
if (recovery.running) return { ok: false, error: errorFromCode(503, "API recovery request is already running") };
|
|
4087
|
+
if (recovery.token !== (recovery.options.token || this.token)) {
|
|
4088
|
+
const error = errorFromCode(401, "Session changed during API recovery");
|
|
4089
|
+
this.cancelRecovery(error);
|
|
4090
|
+
return { ok: false, error };
|
|
4091
|
+
}
|
|
4092
|
+
recovery.running = true;
|
|
4093
|
+
try {
|
|
4094
|
+
const value = await this._requestOnce(
|
|
4095
|
+
deepCopy(recovery.request),
|
|
4096
|
+
{ ...recovery.options, token: recovery.token || void 0 },
|
|
4097
|
+
true
|
|
4098
|
+
);
|
|
4099
|
+
return { ok: true, value };
|
|
4100
|
+
} catch (error) {
|
|
4101
|
+
return { ok: false, error };
|
|
4102
|
+
} finally {
|
|
4103
|
+
if (this.recovery === recovery) recovery.running = false;
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4106
|
+
scheduleRecovery() {
|
|
4107
|
+
const recovery = this.recovery;
|
|
4108
|
+
if (!recovery) return;
|
|
4109
|
+
if (recovery.timer) clearTimeout(recovery.timer);
|
|
4110
|
+
recovery.timer = setTimeout(async () => {
|
|
4111
|
+
const current = this.recovery;
|
|
4112
|
+
if (!current || current !== recovery) return;
|
|
4113
|
+
current.timer = null;
|
|
4114
|
+
const result = await this.tryRecoveryOnce();
|
|
4115
|
+
if (result.ok) {
|
|
4116
|
+
this.finishRecovery();
|
|
4117
|
+
return;
|
|
4118
|
+
}
|
|
4119
|
+
if (this.recovery && this.status !== "unauthorized") this.scheduleRecovery();
|
|
4120
|
+
}, this.recoveryRetryInterval);
|
|
4121
|
+
}
|
|
4122
|
+
finishRecovery() {
|
|
4123
|
+
const recovery = this.recovery;
|
|
4124
|
+
if (!recovery) return;
|
|
4125
|
+
if (recovery.timer) clearTimeout(recovery.timer);
|
|
4126
|
+
this.recovery = null;
|
|
4127
|
+
this.setConnectionStatus("online");
|
|
4128
|
+
}
|
|
4129
|
+
cancelRecovery(_error) {
|
|
4130
|
+
const recovery = this.recovery;
|
|
4131
|
+
if (!recovery) return;
|
|
4132
|
+
if (recovery.timer) clearTimeout(recovery.timer);
|
|
4133
|
+
this.recovery = null;
|
|
4449
4134
|
}
|
|
4450
4135
|
async checkConnection() {
|
|
4136
|
+
if (this.recovery) return;
|
|
4451
4137
|
if (typeof navigator != "undefined" && !navigator.onLine) {
|
|
4452
|
-
this.
|
|
4138
|
+
this.setConnectionStatus("offline");
|
|
4453
4139
|
return;
|
|
4454
4140
|
}
|
|
4455
4141
|
if (this.onlineOverride) return;
|
|
4142
|
+
if (this.authenticationInvalid) {
|
|
4143
|
+
this.setConnectionStatus("unauthorized");
|
|
4144
|
+
return;
|
|
4145
|
+
}
|
|
4146
|
+
if (this.token && this.isTokenExpired(this.token)) {
|
|
4147
|
+
this.markUnauthorized(this.token);
|
|
4148
|
+
return;
|
|
4149
|
+
}
|
|
4456
4150
|
const controller = new AbortController();
|
|
4457
4151
|
const timeout = setTimeout(() => controller.abort(), this.heartbeat.timeout);
|
|
4458
4152
|
try {
|
|
4459
4153
|
const response = await fetch(this.url + this.heartbeat.target, { signal: controller.signal });
|
|
4460
|
-
this.
|
|
4154
|
+
if (this.recovery) return;
|
|
4155
|
+
this.setConnectionStatus(response.ok ? "online" : "unavailable");
|
|
4461
4156
|
} catch (error) {
|
|
4462
|
-
this.
|
|
4157
|
+
if (!this.recovery) this.setConnectionStatus("offline");
|
|
4463
4158
|
} finally {
|
|
4464
4159
|
clearTimeout(timeout);
|
|
4465
4160
|
}
|
|
4466
4161
|
}
|
|
4162
|
+
isTokenExpired(token) {
|
|
4163
|
+
var _a;
|
|
4164
|
+
try {
|
|
4165
|
+
return (((_a = decodeJwt(token)) == null ? void 0 : _a.exp) ?? 0) * 1e3 <= Date.now();
|
|
4166
|
+
} catch {
|
|
4167
|
+
return true;
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
scheduleTokenExpiry(token) {
|
|
4171
|
+
var _a, _b, _c;
|
|
4172
|
+
if (this.tokenExpiryTimeout) clearTimeout(this.tokenExpiryTimeout);
|
|
4173
|
+
this.tokenExpiryTimeout = null;
|
|
4174
|
+
if (!token) return;
|
|
4175
|
+
let expiresAt = 0;
|
|
4176
|
+
try {
|
|
4177
|
+
expiresAt = (((_a = decodeJwt(token)) == null ? void 0 : _a.exp) ?? 0) * 1e3;
|
|
4178
|
+
} catch {
|
|
4179
|
+
return this.markUnauthorized(token);
|
|
4180
|
+
}
|
|
4181
|
+
const delay = expiresAt - Date.now();
|
|
4182
|
+
if (delay <= 0) return this.markUnauthorized(token);
|
|
4183
|
+
this.tokenExpiryTimeout = setTimeout(() => {
|
|
4184
|
+
if (delay > 2147e6) this.scheduleTokenExpiry(token);
|
|
4185
|
+
else this.markUnauthorized(token);
|
|
4186
|
+
}, Math.min(delay, 2147e6));
|
|
4187
|
+
(_c = (_b = this.tokenExpiryTimeout) == null ? void 0 : _b.unref) == null ? void 0 : _c.call(_b);
|
|
4188
|
+
}
|
|
4189
|
+
markUnauthorized(token) {
|
|
4190
|
+
if (token && token !== this.token) return;
|
|
4191
|
+
this.authenticationInvalid = true;
|
|
4192
|
+
this.cancelRecovery(errorFromCode(401, "Session expired"));
|
|
4193
|
+
if (this.token != null) this.token$.next(null);
|
|
4194
|
+
this.setConnectionStatus("unauthorized");
|
|
4195
|
+
}
|
|
4196
|
+
setConnectionStatus(status) {
|
|
4197
|
+
if (this.status !== status) this.status$.next(status);
|
|
4198
|
+
const online = status === "online";
|
|
4199
|
+
if (this.online !== online) this.online$.next(online);
|
|
4200
|
+
}
|
|
4467
4201
|
offlineBanner() {
|
|
4468
4202
|
if (this.options.offlineBanner === false || typeof document == "undefined") return;
|
|
4469
|
-
if (this.online) {
|
|
4203
|
+
if (this.status === "online") {
|
|
4470
4204
|
removeBanner("datalynk-offline-banner");
|
|
4471
4205
|
} else {
|
|
4472
|
-
|
|
4206
|
+
const message = this.status === "unauthorized" ? "⚠️ Your session expired, please sign in again" : this.status === "unavailable" ? "⚠️ Datalynk is currently unavailable" : "⚠️ You are offline, please reconnect to sync changes";
|
|
4207
|
+
createBanner(message, {
|
|
4473
4208
|
id: "datalynk-offline-banner",
|
|
4474
4209
|
position: this.options.offlineBanner === "top" ? "top" : "bottom"
|
|
4475
4210
|
});
|
|
@@ -4581,9 +4316,13 @@ const _Api = class _Api {
|
|
|
4581
4316
|
var _a, _b;
|
|
4582
4317
|
data = typeof data == "string" ? { [data]: {} } : data;
|
|
4583
4318
|
let key = JSON.stringify(data);
|
|
4584
|
-
if (this.offline) {
|
|
4585
|
-
|
|
4586
|
-
|
|
4319
|
+
if (this.offline && typeof navigator != "undefined") {
|
|
4320
|
+
if (this.status === "unauthorized") return Promise.reject(errorFromCode(401, "Session expired"));
|
|
4321
|
+
if (options.offline) {
|
|
4322
|
+
(_b = (_a = this.database) == null ? void 0 : _a.table("pending")) == null ? void 0 : _b.add(data, key);
|
|
4323
|
+
return Promise.resolve();
|
|
4324
|
+
}
|
|
4325
|
+
return Promise.reject(errorFromCode(503, this.status === "unavailable" ? "Datalynk is unavailable" : "You are offline"));
|
|
4587
4326
|
}
|
|
4588
4327
|
if (options.noOptimize) {
|
|
4589
4328
|
return new Promise((res, rej) => {
|
|
@@ -4594,7 +4333,7 @@ const _Api = class _Api {
|
|
|
4594
4333
|
}
|
|
4595
4334
|
if (!this.pending[key]) {
|
|
4596
4335
|
this.pending[key] = new Promise((res, rej) => this.bundle.push({ data, res, rej }));
|
|
4597
|
-
this.pending[key].
|
|
4336
|
+
this.pending[key].then(() => delete this.pending[key], () => delete this.pending[key]);
|
|
4598
4337
|
if (!this.bundleOngoing) {
|
|
4599
4338
|
this.bundleOngoing = true;
|
|
4600
4339
|
setTimeout(() => {
|
|
@@ -4604,7 +4343,10 @@ const _Api = class _Api {
|
|
|
4604
4343
|
data = originalBundle.map((row) => row.data);
|
|
4605
4344
|
this._request(data, options).then((resp) => {
|
|
4606
4345
|
if (!(resp instanceof Array)) resp = [resp];
|
|
4607
|
-
|
|
4346
|
+
originalBundle.forEach((request, index) => {
|
|
4347
|
+
const row = resp[index];
|
|
4348
|
+
(row == null ? void 0 : row.error) ? request.rej(row.error) : request.res(row);
|
|
4349
|
+
});
|
|
4608
4350
|
}).catch((err) => originalBundle.forEach((req) => req.rej(err)));
|
|
4609
4351
|
}, this.options.bundleTime);
|
|
4610
4352
|
}
|
|
@@ -4645,5 +4387,6 @@ export {
|
|
|
4645
4387
|
Slice,
|
|
4646
4388
|
Socket,
|
|
4647
4389
|
Superuser,
|
|
4390
|
+
UnexpectedApiResponseError,
|
|
4648
4391
|
getTheme
|
|
4649
4392
|
};
|