@auxilium/datalynk-client 1.3.5 โ 1.3.7
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 +56 -15
- package/dist/api.d.ts +13 -5
- package/dist/api.d.ts.map +1 -1
- package/dist/banner.d.ts +8 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/index.cjs +118 -89
- package/dist/index.mjs +118 -89
- package/dist/pwa.d.ts +2 -1
- package/dist/pwa.d.ts.map +1 -1
- package/dist/service.worker.mjs +128 -0
- package/dist/slice.d.ts +6 -4
- package/dist/slice.d.ts.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -264,32 +264,73 @@ const user = await api.auth.login('spoke', 'username', 'password', '2faCode');
|
|
|
264
264
|
<h3 id="pwa" style="display: inline">Offline / PWA</h3>
|
|
265
265
|
</summary>
|
|
266
266
|
|
|
267
|
-
|
|
267
|
+
### Support
|
|
268
268
|
|
|
269
|
-
|
|
269
|
+
#### PWA
|
|
270
|
+
Turns the website into an installable native app
|
|
271
|
+
- The PWA manifest can be configured with: `manifest`
|
|
272
|
+
- The install prompt can be configured with: `pwaSettings`
|
|
273
|
+
```ts
|
|
274
|
+
const api = new Api(`https://${spoke}.auxiliumgroup.com`, {
|
|
275
|
+
name: "Workplace Occupational Health & Safety Inspection", // REQUIRED: App name
|
|
276
|
+
manifest: { // Set/Override any manifest values
|
|
277
|
+
scope: 'https://lynk-forms.scarborough.auxilium.world/OshawaCL/ohsinspection.html'
|
|
278
|
+
},
|
|
279
|
+
pwaSettings: { // PWA install prompt settings
|
|
280
|
+
timeout: 45, // seconds before prompt shows (default: 30)
|
|
281
|
+
dismissExpiry: 3, // days before prompt shows again (0 = every refresh, default: 7)
|
|
282
|
+
loginLink: true // Show install link in login screen
|
|
283
|
+
},
|
|
284
|
+
});
|
|
270
285
|
|
|
271
|
-
|
|
286
|
+
// Manually trigger
|
|
287
|
+
api.pwa.prompt();
|
|
288
|
+
```
|
|
272
289
|
|
|
273
|
-
|
|
290
|
+
#### Static assets
|
|
291
|
+
Upload the [service worker](https://datalynk-client.scarborough.auxilium.world/dist/service.worker.mjs) `dist/service.worker.mjs` to the *root* directory of the server
|
|
292
|
+
to cache all files for use offline
|
|
293
|
+
```ts
|
|
294
|
+
const api = new Api(`https://${spoke}.auxiliumgroup.com`, {
|
|
295
|
+
serviceWorker: 'https://.../service.worker.mjs', // Shouldnt be needed but can be overriden
|
|
296
|
+
});
|
|
297
|
+
```
|
|
274
298
|
|
|
275
|
-
|
|
299
|
+
#### Slice Engine
|
|
300
|
+
Slices can be used as normal offline using the slice engine by marking the slice as offline with: `offline`
|
|
276
301
|
```ts
|
|
277
302
|
const api = new Api(`https://${spoke}.auxiliumgroup.com`, {
|
|
278
|
-
name: "Workplace Occupational Health & Safety Inspection", // REQUIRED: App name
|
|
279
303
|
offline: [51306, 51531, 51563], // REQUIRED: Specify slices that must be stored offline
|
|
280
|
-
manifest: { scope: 'https://lynk-forms.scarborough.auxilium.world/OshawaCL/ohsinspection.html' }, // Any manifest overrides suck as PWA "scope"
|
|
281
|
-
pwaSettings: { // PWA install prompt settings
|
|
282
|
-
timeout: 45, // seconds before prompt shows (default: 30)
|
|
283
|
-
dismissExpiry: 3, // days before prompt shows again (0 = every refresh, default: 7)
|
|
284
|
-
loginLink: true // Show install link in login screen
|
|
285
|
-
},
|
|
286
|
-
serviceWorker: 'https://.../service.worker.mjs', // Override service worker URL, must be on same domain (Step 1)
|
|
287
304
|
socket: true // Make sure sockets are enabled
|
|
288
305
|
});
|
|
306
|
+
await api.slice(51306).insert({...}).exec();
|
|
307
|
+
const resp = await api.slice(51306).select().where('id', '<=', 100).exec();
|
|
308
|
+
```
|
|
309
|
+
#### API Requests
|
|
310
|
+
Any API request can be deferred until online, however you dont get responses when offline
|
|
311
|
+
- Only useful for submitting data or when we dont care about the response
|
|
312
|
+
```ts
|
|
313
|
+
api.request(..., {offline: true}).then((resp: T | void) => {
|
|
314
|
+
if(resp != null) {
|
|
315
|
+
// Online logic (has response payload)
|
|
316
|
+
} else {
|
|
317
|
+
// Offline logic (no response payload)
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
</details>
|
|
289
322
|
|
|
290
|
-
|
|
291
|
-
|
|
323
|
+
<details>
|
|
324
|
+
<summary>
|
|
325
|
+
<h3 id="pdf" style="display: inline">PDFs</h3>
|
|
326
|
+
</summary>
|
|
327
|
+
|
|
328
|
+
PDFs can be generated using the API. PDFs can be optionally related to a record by passing an association
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
const resp = await api.pdf.fromUrl('https://google.com', {slice: 12345, row: 1, field: 'resume'});
|
|
292
332
|
```
|
|
333
|
+
|
|
293
334
|
</details>
|
|
294
335
|
|
|
295
336
|
<details>
|
package/dist/api.d.ts
CHANGED
|
@@ -58,9 +58,9 @@ export type ApiOptions = {
|
|
|
58
58
|
};
|
|
59
59
|
/** Progressive Web App settings */
|
|
60
60
|
pwaSettings?: {
|
|
61
|
-
/** How long to wait before showing
|
|
61
|
+
/** How long to wait before showing the installation prompt (seconds). Default is 30 seconds */
|
|
62
62
|
timeout?: number;
|
|
63
|
-
/** Days until dismissed prompt can show again. If set to 0 it will trigger every refresh. (Default is 7 days) */
|
|
63
|
+
/** Days until the dismissed prompt can show again. If set to `0` it will trigger every refresh. (Default is 7 days) */
|
|
64
64
|
dismissExpiry?: number;
|
|
65
65
|
/** Show PWA install link on login screen. Default is False */
|
|
66
66
|
loginLink?: boolean;
|
|
@@ -72,6 +72,8 @@ export type ApiOptions = {
|
|
|
72
72
|
export interface ApiRequestOptions {
|
|
73
73
|
/** Skip bundling & caching */
|
|
74
74
|
noOptimize?: boolean;
|
|
75
|
+
/** Queue for network connection */
|
|
76
|
+
offline?: boolean;
|
|
75
77
|
/** Skip the token translating step */
|
|
76
78
|
raw?: boolean;
|
|
77
79
|
/** Set request token */
|
|
@@ -125,7 +127,7 @@ export declare class Api {
|
|
|
125
127
|
/** Options */
|
|
126
128
|
options: ApiOptions;
|
|
127
129
|
/** Created slices */
|
|
128
|
-
sliceCache: Map<number, Slice
|
|
130
|
+
sliceCache: Map<number, Slice>;
|
|
129
131
|
/** API URL */
|
|
130
132
|
url: string;
|
|
131
133
|
/** Client library version */
|
|
@@ -135,7 +137,7 @@ export declare class Api {
|
|
|
135
137
|
/** Get session info from JWT payload */
|
|
136
138
|
get jwtPayload(): JwtPayload | null;
|
|
137
139
|
private onlineOverride;
|
|
138
|
-
|
|
140
|
+
online$: BehaviorSubject<boolean>;
|
|
139
141
|
/** Check if we are connected */
|
|
140
142
|
get online(): boolean | null;
|
|
141
143
|
get offline(): boolean;
|
|
@@ -192,13 +194,16 @@ export declare class Api {
|
|
|
192
194
|
[key: string]: any;
|
|
193
195
|
}): Promise<any>;
|
|
194
196
|
/**
|
|
195
|
-
* Exact same as `request` method, but logs the response in the console automatically
|
|
197
|
+
* Exact same as the `request` method, but logs the response in the console automatically
|
|
196
198
|
*
|
|
197
199
|
* @param {object | string} data Datalynk request as object or string
|
|
198
200
|
* @param {ApiRequestOptions} options
|
|
199
201
|
* @returns {Promise<any>} Datalynk response
|
|
200
202
|
*/
|
|
201
203
|
debug<T = any>(data: any, options?: ApiRequestOptions): Promise<T>;
|
|
204
|
+
debug<T = any>(data: any, options: {
|
|
205
|
+
offline: true;
|
|
206
|
+
} & ApiRequestOptions): Promise<T | void>;
|
|
202
207
|
/**
|
|
203
208
|
* Send a request to Datalynk
|
|
204
209
|
*
|
|
@@ -212,6 +217,9 @@ export declare class Api {
|
|
|
212
217
|
* @returns {Promise<any>} Datalynk response or error
|
|
213
218
|
*/
|
|
214
219
|
request<T = any>(data: any, options?: ApiRequestOptions): Promise<T>;
|
|
220
|
+
request<T = any>(data: any, options: {
|
|
221
|
+
offline: true;
|
|
222
|
+
} & ApiRequestOptions): Promise<T | void>;
|
|
215
223
|
/**
|
|
216
224
|
* Create a slice object using the API
|
|
217
225
|
*
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoD,QAAQ,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/banner.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type BannerOptions = {
|
|
2
|
+
color?: string;
|
|
3
|
+
position?: 'top' | 'bottom';
|
|
4
|
+
id?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function createBanner(message: string, options?: BannerOptions): void;
|
|
7
|
+
export declare function removeBanner(id?: string): void;
|
|
8
|
+
//# sourceMappingURL=banner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../src/banner.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;CACZ,CAAA;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,QAyBxE;AAED,wBAAgB,YAAY,CAAC,EAAE,SAAoB,QAGlD"}
|
package/dist/index.cjs
CHANGED
|
@@ -1783,13 +1783,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1783
1783
|
pwaLink.addEventListener("click", async (e) => {
|
|
1784
1784
|
var _a;
|
|
1785
1785
|
e.preventDefault();
|
|
1786
|
-
if ((_a = this.api.pwa) == null ? void 0 : _a.
|
|
1787
|
-
this.api.pwa.
|
|
1788
|
-
const choice = await this.api.pwa.
|
|
1786
|
+
if ((_a = this.api.pwa) == null ? void 0 : _a.nativeInstallPrompt) {
|
|
1787
|
+
this.api.pwa.nativeInstallPrompt.prompt();
|
|
1788
|
+
const choice = await this.api.pwa.nativeInstallPrompt.userChoice;
|
|
1789
1789
|
if (choice.outcome === "accepted") {
|
|
1790
1790
|
console.log("PWA installed via login link");
|
|
1791
1791
|
}
|
|
1792
|
-
this.api.pwa.
|
|
1792
|
+
this.api.pwa.nativeInstallPrompt = null;
|
|
1793
1793
|
} else {
|
|
1794
1794
|
this.api.pwa.prompt();
|
|
1795
1795
|
}
|
|
@@ -2309,12 +2309,42 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2309
2309
|
return this.api.request({ "$/auth/mobile/generate": { user: login, method: mode } });
|
|
2310
2310
|
}
|
|
2311
2311
|
}
|
|
2312
|
+
function createBanner(message, options = {}) {
|
|
2313
|
+
options = {
|
|
2314
|
+
color: "#dc3545",
|
|
2315
|
+
position: "top",
|
|
2316
|
+
id: "datalynk-banner",
|
|
2317
|
+
...options
|
|
2318
|
+
};
|
|
2319
|
+
removeBanner(options.id);
|
|
2320
|
+
const banner = document.createElement("div");
|
|
2321
|
+
banner.className = options.id;
|
|
2322
|
+
Object.assign(banner.style, {
|
|
2323
|
+
position: "fixed",
|
|
2324
|
+
[options.position === "top" ? "top" : "bottom"]: "0",
|
|
2325
|
+
left: "0",
|
|
2326
|
+
right: "0",
|
|
2327
|
+
padding: ".75rem",
|
|
2328
|
+
fontSize: "1rem",
|
|
2329
|
+
fontWeight: "bolder",
|
|
2330
|
+
backgroundColor: options.color,
|
|
2331
|
+
color: "#fff",
|
|
2332
|
+
textAlign: "center",
|
|
2333
|
+
zIndex: "9999"
|
|
2334
|
+
});
|
|
2335
|
+
banner.innerHTML = message;
|
|
2336
|
+
document.body.appendChild(banner);
|
|
2337
|
+
}
|
|
2338
|
+
function removeBanner(id = "datalynk-banner") {
|
|
2339
|
+
const banner = document.querySelector(`.${id}`);
|
|
2340
|
+
if (banner) banner.remove();
|
|
2341
|
+
}
|
|
2312
2342
|
class PWA {
|
|
2313
2343
|
constructor(api) {
|
|
2314
|
-
|
|
2344
|
+
/** Capture Install Event */
|
|
2345
|
+
__publicField(this, "nativeInstallPrompt", null);
|
|
2315
2346
|
this.api = api;
|
|
2316
2347
|
}
|
|
2317
|
-
// Holds the beforeinstallprompt event
|
|
2318
2348
|
get headless() {
|
|
2319
2349
|
return typeof window == "undefined";
|
|
2320
2350
|
}
|
|
@@ -2341,34 +2371,36 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2341
2371
|
if (!installBtn) return;
|
|
2342
2372
|
installBtn.onclick = async () => {
|
|
2343
2373
|
var _a;
|
|
2344
|
-
this.
|
|
2345
|
-
|
|
2374
|
+
if (!this.nativeInstallPrompt) return console.warn("PWA is not supported");
|
|
2375
|
+
this.nativeInstallPrompt.prompt();
|
|
2376
|
+
const choice = await this.nativeInstallPrompt.userChoice;
|
|
2346
2377
|
if (choice.outcome === "accepted") {
|
|
2347
2378
|
console.log("PWA installed");
|
|
2348
|
-
localStorage.
|
|
2379
|
+
localStorage.setItem(`${this.api.options.name}:pwa`, "installed");
|
|
2380
|
+
} else {
|
|
2381
|
+
localStorage.setItem(`${this.api.options.name}:pwa`, Date.now().toString());
|
|
2349
2382
|
}
|
|
2350
|
-
this.deferredPrompt = null;
|
|
2351
2383
|
(_a = document.querySelector(".pwa-prompt")) == null ? void 0 : _a.remove();
|
|
2352
2384
|
};
|
|
2353
2385
|
}
|
|
2354
2386
|
/** Setup the PWA */
|
|
2355
2387
|
async setup(manifest = {}) {
|
|
2356
|
-
var _a, _b, _c;
|
|
2357
2388
|
window.addEventListener("beforeinstallprompt", (e) => {
|
|
2358
2389
|
e.preventDefault();
|
|
2359
|
-
this.
|
|
2390
|
+
this.nativeInstallPrompt = e;
|
|
2360
2391
|
const actions = document.querySelector(".pwa-prompt-actions");
|
|
2361
2392
|
if (actions && !actions.querySelector("#installPwaBtn")) {
|
|
2362
2393
|
actions.innerHTML = `<button id="installPwaBtn">Install App</button>`;
|
|
2363
2394
|
this.bindInstallButton();
|
|
2364
2395
|
}
|
|
2365
2396
|
});
|
|
2366
|
-
|
|
2367
|
-
timeout:
|
|
2368
|
-
//
|
|
2369
|
-
dismissExpiry:
|
|
2370
|
-
//
|
|
2371
|
-
loginLink:
|
|
2397
|
+
this.api.options.pwaSettings = {
|
|
2398
|
+
timeout: 30,
|
|
2399
|
+
// Seconds
|
|
2400
|
+
dismissExpiry: 7,
|
|
2401
|
+
// Days
|
|
2402
|
+
loginLink: false,
|
|
2403
|
+
...this.api.options.pwaSettings
|
|
2372
2404
|
};
|
|
2373
2405
|
const meta = (key, name, value) => {
|
|
2374
2406
|
const exists = document.querySelector(`meta[${key}="${name}"]`);
|
|
@@ -2402,19 +2434,16 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2402
2434
|
document.head.append(link);
|
|
2403
2435
|
}
|
|
2404
2436
|
if (!this.headless && !this.iframe && !this.pwa && this.platform != "mac") setTimeout(() => {
|
|
2405
|
-
|
|
2406
|
-
if (
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2411
|
-
}
|
|
2437
|
+
var _a, _b, _c, _d;
|
|
2438
|
+
if (((_b = (_a = this.api.options) == null ? void 0 : _a.pwaSettings) == null ? void 0 : _b.dismissExpiry) === 0) localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2439
|
+
let dismissedDate = localStorage.getItem(`${this.api.options.name}:pwa`);
|
|
2440
|
+
if (dismissedDate) dismissedDate = JSON.parse(dismissedDate);
|
|
2441
|
+
if (!!dismissedDate && Date.now() > dismissedDate + ((_d = (_c = this.api.options) == null ? void 0 : _c.pwaSettings) == null ? void 0 : _d.dismissExpiry) * 6e4 * 60 * 24) return;
|
|
2412
2442
|
this.prompt();
|
|
2413
|
-
},
|
|
2443
|
+
}, this.api.options.pwaSettings.timeout);
|
|
2414
2444
|
}
|
|
2415
2445
|
/** Prompt user to install the app */
|
|
2416
2446
|
async prompt(platform) {
|
|
2417
|
-
var _a;
|
|
2418
2447
|
if (document.querySelector(".pwa-prompt")) return;
|
|
2419
2448
|
const android = (platform || this.platform) == "android";
|
|
2420
2449
|
let style = document.querySelector("style.pwa");
|
|
@@ -2509,10 +2538,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2509
2538
|
<button class="pwa-prompt-close">×</button>
|
|
2510
2539
|
</div>
|
|
2511
2540
|
<div class="pwa-prompt-body">
|
|
2512
|
-
${this.
|
|
2541
|
+
${this.nativeInstallPrompt ? "Click below to install the app directly to your device." : "Add this app to your home screen for a faster, fullscreen experience."}
|
|
2513
2542
|
</div>
|
|
2514
2543
|
<div class="pwa-prompt-actions">
|
|
2515
|
-
${this.
|
|
2544
|
+
${this.nativeInstallPrompt ? `<button id="installPwaBtn">Install App</button>` : `
|
|
2516
2545
|
<table>
|
|
2517
2546
|
<tr>
|
|
2518
2547
|
<td style="width:40px;text-align:center">
|
|
@@ -2531,13 +2560,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2531
2560
|
</div>
|
|
2532
2561
|
`;
|
|
2533
2562
|
const closeBtn = prompt.querySelector(".pwa-prompt-close");
|
|
2534
|
-
const dismissExpiry = ((_a = this.api.options.pwaSettings) == null ? void 0 : _a.dismissExpiry) ?? 7;
|
|
2535
2563
|
closeBtn.onclick = () => {
|
|
2536
|
-
|
|
2564
|
+
var _a;
|
|
2565
|
+
!!((_a = this.api.options.pwaSettings) == null ? void 0 : _a.dismissExpiry) ? localStorage.setItem(`${this.api.options.name}:pwa`, Date.now().toString()) : localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2537
2566
|
prompt.remove();
|
|
2538
2567
|
};
|
|
2539
2568
|
document.body.append(prompt);
|
|
2540
|
-
if (this.
|
|
2569
|
+
if (this.nativeInstallPrompt) this.bindInstallButton();
|
|
2541
2570
|
}
|
|
2542
2571
|
}
|
|
2543
2572
|
class Files {
|
|
@@ -3066,7 +3095,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3066
3095
|
return this;
|
|
3067
3096
|
}
|
|
3068
3097
|
}
|
|
3069
|
-
class
|
|
3098
|
+
const _Slice = class _Slice {
|
|
3070
3099
|
/**
|
|
3071
3100
|
* An object to aid in constructing requests
|
|
3072
3101
|
*
|
|
@@ -3077,17 +3106,20 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3077
3106
|
__publicField(this, "table");
|
|
3078
3107
|
__publicField(this, "info");
|
|
3079
3108
|
__publicField(this, "loaded", false);
|
|
3080
|
-
__publicField(this, "pendingInsert", 0);
|
|
3081
|
-
/** Unsubscribe from changes, undefined if not subscribed */
|
|
3082
|
-
__publicField(this, "unsubscribe");
|
|
3083
3109
|
/** Cached slice data as an observable */
|
|
3084
3110
|
__publicField(this, "cache$", new BehaviorSubject([]));
|
|
3111
|
+
/** Unsubscribe from changes, undefined if not subscribed */
|
|
3112
|
+
__publicField(this, "unsubscribe");
|
|
3085
3113
|
var _a;
|
|
3086
3114
|
this.slice = slice;
|
|
3087
3115
|
this.api = api;
|
|
3088
3116
|
if (this.offlineEnabled) {
|
|
3089
3117
|
this.table = (_a = api.database) == null ? void 0 : _a.table(slice.toString());
|
|
3090
|
-
this.table.getAll().then((resp) =>
|
|
3118
|
+
this.table.getAll().then((resp) => {
|
|
3119
|
+
this.cache = resp;
|
|
3120
|
+
const lowest = resp.reduce((acc, r) => Math.min(acc, r.id), 0);
|
|
3121
|
+
if (lowest < _Slice.nextId) _Slice.nextId = lowest;
|
|
3122
|
+
}).finally(() => this.loaded = true);
|
|
3091
3123
|
this.cache$.pipe(skip(1)).subscribe(async (cache) => {
|
|
3092
3124
|
var _a2;
|
|
3093
3125
|
await ((_a2 = this.table) == null ? void 0 : _a2.clear());
|
|
@@ -3095,10 +3127,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3095
3127
|
var _a3;
|
|
3096
3128
|
return (_a3 = this.table) == null ? void 0 : _a3.put(c.id, c);
|
|
3097
3129
|
}));
|
|
3098
|
-
this.fixIncrement();
|
|
3099
3130
|
});
|
|
3100
3131
|
this.sync();
|
|
3101
|
-
|
|
3132
|
+
this.api.online$.subscribe(async (online) => {
|
|
3133
|
+
if (!online) return;
|
|
3102
3134
|
if (this.api.expired) await lastValueFrom(this.api.auth.user$.pipe(skip(1), takeWhile((u) => !u || this.api.expired, true)));
|
|
3103
3135
|
this.pushChanges();
|
|
3104
3136
|
});
|
|
@@ -3119,10 +3151,6 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3119
3151
|
var _a;
|
|
3120
3152
|
return (_a = this.api.database) == null ? void 0 : _a.includes(this.slice.toString());
|
|
3121
3153
|
}
|
|
3122
|
-
fixIncrement() {
|
|
3123
|
-
var _a;
|
|
3124
|
-
this.pendingInsert = ((_a = this.cache.toSorted(sortByProp("id")).pop()) == null ? void 0 : _a["id"]) || 0;
|
|
3125
|
-
}
|
|
3126
3154
|
execWrapper(call) {
|
|
3127
3155
|
const onlineExec = call.exec.bind(call);
|
|
3128
3156
|
return async () => {
|
|
@@ -3168,7 +3196,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3168
3196
|
} else if (request["$/slice/xinsert"]) {
|
|
3169
3197
|
const rows = request["$/slice/xinsert"]["rows"].map((r) => ({
|
|
3170
3198
|
...r,
|
|
3171
|
-
id:
|
|
3199
|
+
id: --_Slice.nextId,
|
|
3172
3200
|
_sync: "insert"
|
|
3173
3201
|
}));
|
|
3174
3202
|
this.cache = [...this.cache, ...rows];
|
|
@@ -3334,7 +3362,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3334
3362
|
call.exec = this.execWrapper(call);
|
|
3335
3363
|
return call.update(rows);
|
|
3336
3364
|
}
|
|
3337
|
-
}
|
|
3365
|
+
};
|
|
3366
|
+
__publicField(_Slice, "idMap", {});
|
|
3367
|
+
__publicField(_Slice, "nextId", 0);
|
|
3368
|
+
let Slice = _Slice;
|
|
3338
3369
|
class Socket {
|
|
3339
3370
|
constructor(api, options = {}) {
|
|
3340
3371
|
__publicField(this, "listeners", []);
|
|
@@ -3469,7 +3500,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3469
3500
|
} });
|
|
3470
3501
|
}
|
|
3471
3502
|
}
|
|
3472
|
-
const version = "1.3.
|
|
3503
|
+
const version = "1.3.7";
|
|
3473
3504
|
class WebRtc {
|
|
3474
3505
|
constructor(api) {
|
|
3475
3506
|
__publicField(this, "ice");
|
|
@@ -3643,7 +3674,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3643
3674
|
/** Client library version */
|
|
3644
3675
|
__publicField(this, "version", version);
|
|
3645
3676
|
__publicField(this, "onlineOverride", false);
|
|
3646
|
-
__publicField(this, "
|
|
3677
|
+
__publicField(this, "online$", new BehaviorSubject(typeof navigator == "undefined" ? true : navigator.onLine));
|
|
3647
3678
|
/** API Session token */
|
|
3648
3679
|
__publicField(this, "token$", new BehaviorSubject(void 0));
|
|
3649
3680
|
var _a, _b;
|
|
@@ -3679,20 +3710,28 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3679
3710
|
this.pwa = new PWA(this);
|
|
3680
3711
|
this.superuser = new Superuser(this);
|
|
3681
3712
|
this.webrtc = new WebRtc(this);
|
|
3713
|
+
this.database = new Database("datalynk", ["pending", ...this.options.offline || []]);
|
|
3714
|
+
this.online$.subscribe(async (online) => {
|
|
3715
|
+
var _a2;
|
|
3716
|
+
if (!online) return;
|
|
3717
|
+
const table = (_a2 = this.database) == null ? void 0 : _a2.table("pending");
|
|
3718
|
+
const keys = await (table == null ? void 0 : table.getAllKeys());
|
|
3719
|
+
await Promise.allSettled(keys.map(async (k) => {
|
|
3720
|
+
const r = await (table == null ? void 0 : table.get(k));
|
|
3721
|
+
await this.request(r).then(() => table == null ? void 0 : table.delete(k));
|
|
3722
|
+
}));
|
|
3723
|
+
});
|
|
3682
3724
|
if (typeof window !== "undefined") {
|
|
3683
|
-
const handleOffline = (state) => {
|
|
3684
|
-
this.
|
|
3685
|
-
this.offlineBanner();
|
|
3725
|
+
const handleOffline = (state = this.offline) => {
|
|
3726
|
+
if (this.online != state) this.online$.next(state);
|
|
3686
3727
|
};
|
|
3687
3728
|
window.addEventListener("online", () => handleOffline(true));
|
|
3688
3729
|
window.addEventListener("offline", () => handleOffline(false));
|
|
3689
|
-
this.
|
|
3690
|
-
this.offlineBanner();
|
|
3730
|
+
this.online$.subscribe(() => this.offlineBanner());
|
|
3691
3731
|
}
|
|
3692
3732
|
if ((_a = this.options.offline) == null ? void 0 : _a.length) {
|
|
3693
3733
|
this.pwa.setup();
|
|
3694
3734
|
if (typeof indexedDB == "undefined") throw new Error("Cannot enable offline support, indexedDB is not available in this environment");
|
|
3695
|
-
this.database = new Database("datalynk", this.options.offline);
|
|
3696
3735
|
(_b = this.options.offline) == null ? void 0 : _b.forEach((id) => this.slice(id));
|
|
3697
3736
|
if (this.options.serviceWorker && typeof navigator["serviceWorker"] != "undefined") {
|
|
3698
3737
|
(async () => {
|
|
@@ -3725,24 +3764,23 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3725
3764
|
}
|
|
3726
3765
|
/** Check if we are connected */
|
|
3727
3766
|
get online() {
|
|
3728
|
-
return this.
|
|
3767
|
+
return this.online$.getValue();
|
|
3729
3768
|
}
|
|
3730
3769
|
get offline() {
|
|
3731
|
-
return !this.
|
|
3770
|
+
return !this.online;
|
|
3732
3771
|
}
|
|
3733
3772
|
/** Override connection status */
|
|
3734
3773
|
set online(value) {
|
|
3735
3774
|
if (value == null) {
|
|
3736
3775
|
this.onlineOverride = false;
|
|
3737
|
-
this.
|
|
3776
|
+
this.online$.next(typeof navigator == "undefined" ? true : navigator.onLine);
|
|
3738
3777
|
} else {
|
|
3739
3778
|
this.onlineOverride = true;
|
|
3740
3779
|
if (value == this.online) return;
|
|
3741
|
-
this.
|
|
3780
|
+
this.online$.next(value);
|
|
3742
3781
|
if (value) this.startHeartbeat();
|
|
3743
3782
|
else this.stopHeartbeat();
|
|
3744
3783
|
}
|
|
3745
|
-
this.offlineBanner();
|
|
3746
3784
|
}
|
|
3747
3785
|
/** Logged in spoke */
|
|
3748
3786
|
get spoke() {
|
|
@@ -3766,6 +3804,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3766
3804
|
}),
|
|
3767
3805
|
body: JSON.stringify(_Api.translateTokens(req))
|
|
3768
3806
|
}).then(async (resp) => {
|
|
3807
|
+
const warning = resp.headers["X-Warning"];
|
|
3808
|
+
if (warning) console.warn(warning);
|
|
3809
|
+
const banner = resp.headers["X-User-Notice"];
|
|
3810
|
+
if (warning) {
|
|
3811
|
+
createBanner(banner);
|
|
3812
|
+
setTimeout(() => removeBanner(), 1e4);
|
|
3813
|
+
}
|
|
3769
3814
|
this.online = true;
|
|
3770
3815
|
let data = JSONAttemptParse(await resp.text());
|
|
3771
3816
|
if (!resp.ok || (data == null ? void 0 : data.error)) throw Object.assign(errorFromCode(resp.status, data.error), data);
|
|
@@ -3775,7 +3820,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3775
3820
|
}
|
|
3776
3821
|
async checkConnection() {
|
|
3777
3822
|
if (typeof navigator != "undefined" && !navigator.onLine) {
|
|
3778
|
-
this.
|
|
3823
|
+
this.online$.next(false);
|
|
3779
3824
|
return;
|
|
3780
3825
|
}
|
|
3781
3826
|
if (this.onlineOverride) return;
|
|
@@ -3783,24 +3828,22 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3783
3828
|
const timeout = setTimeout(() => controller.abort(), this.heartbeat.timeout);
|
|
3784
3829
|
try {
|
|
3785
3830
|
const response = await fetch(this.url + this.heartbeat.target, { signal: controller.signal });
|
|
3786
|
-
this.
|
|
3831
|
+
this.online$.next(response.ok);
|
|
3787
3832
|
} catch (error) {
|
|
3788
|
-
this.
|
|
3833
|
+
this.online$.next(false);
|
|
3789
3834
|
} finally {
|
|
3790
3835
|
clearTimeout(timeout);
|
|
3791
3836
|
}
|
|
3792
3837
|
}
|
|
3793
3838
|
offlineBanner() {
|
|
3794
3839
|
if (this.options.offlineBanner === false || typeof document == "undefined") return;
|
|
3795
|
-
const banner = document.querySelector(".datalynk-offline-banner");
|
|
3796
3840
|
if (this.online) {
|
|
3797
|
-
banner
|
|
3798
|
-
} else
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
document.body.appendChild(b);
|
|
3841
|
+
removeBanner("datalynk-offline-banner");
|
|
3842
|
+
} else {
|
|
3843
|
+
createBanner("โ ๏ธ You are offline, please reconnect to sync changes", {
|
|
3844
|
+
id: "datalynk-offline-banner",
|
|
3845
|
+
position: this.options.offlineBanner === "top" ? "top" : "bottom"
|
|
3846
|
+
});
|
|
3804
3847
|
}
|
|
3805
3848
|
}
|
|
3806
3849
|
startHeartbeat() {
|
|
@@ -3896,13 +3939,6 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3896
3939
|
{ "$_": "*" }
|
|
3897
3940
|
] }, {});
|
|
3898
3941
|
}
|
|
3899
|
-
/**
|
|
3900
|
-
* Exact same as `request` method, but logs the response in the console automatically
|
|
3901
|
-
*
|
|
3902
|
-
* @param {object | string} data Datalynk request as object or string
|
|
3903
|
-
* @param {ApiRequestOptions} options
|
|
3904
|
-
* @returns {Promise<any>} Datalynk response
|
|
3905
|
-
*/
|
|
3906
3942
|
debug(data, options = {}) {
|
|
3907
3943
|
return this.request(data, options).then((data2) => {
|
|
3908
3944
|
console.log(data2);
|
|
@@ -3912,20 +3948,14 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3912
3948
|
return err;
|
|
3913
3949
|
});
|
|
3914
3950
|
}
|
|
3915
|
-
/**
|
|
3916
|
-
* Send a request to Datalynk
|
|
3917
|
-
*
|
|
3918
|
-
* @example
|
|
3919
|
-
* ```ts
|
|
3920
|
-
* const response = await api.request('$/auth/current');
|
|
3921
|
-
* ```
|
|
3922
|
-
*
|
|
3923
|
-
* @param {object} data Request using Datalynk API syntax. Strings will be converted: '$/auth/current' -> {'$/auth/current': {}}
|
|
3924
|
-
* @param {ApiRequestOptions} options
|
|
3925
|
-
* @returns {Promise<any>} Datalynk response or error
|
|
3926
|
-
*/
|
|
3927
3951
|
request(data, options = {}) {
|
|
3952
|
+
var _a, _b;
|
|
3928
3953
|
data = typeof data == "string" ? { [data]: {} } : data;
|
|
3954
|
+
let key = JSON.stringify(data);
|
|
3955
|
+
if (this.offline) {
|
|
3956
|
+
(_b = (_a = this.database) == null ? void 0 : _a.table("pending")) == null ? void 0 : _b.add(data, key);
|
|
3957
|
+
return Promise.resolve();
|
|
3958
|
+
}
|
|
3929
3959
|
if (options.noOptimize) {
|
|
3930
3960
|
return new Promise((res, rej) => {
|
|
3931
3961
|
this._request(data, options).then((resp) => {
|
|
@@ -3933,7 +3963,6 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3933
3963
|
}).catch((err) => rej(err));
|
|
3934
3964
|
});
|
|
3935
3965
|
}
|
|
3936
|
-
let key = JSON.stringify(data);
|
|
3937
3966
|
if (!this.pending[key]) {
|
|
3938
3967
|
this.pending[key] = new Promise((res, rej) => this.bundle.push({ data, res, rej }));
|
|
3939
3968
|
this.pending[key].catch().then(() => delete this.pending[key]);
|
package/dist/index.mjs
CHANGED
|
@@ -1779,13 +1779,13 @@ const _LoginPrompt = class _LoginPrompt {
|
|
|
1779
1779
|
pwaLink.addEventListener("click", async (e) => {
|
|
1780
1780
|
var _a;
|
|
1781
1781
|
e.preventDefault();
|
|
1782
|
-
if ((_a = this.api.pwa) == null ? void 0 : _a.
|
|
1783
|
-
this.api.pwa.
|
|
1784
|
-
const choice = await this.api.pwa.
|
|
1782
|
+
if ((_a = this.api.pwa) == null ? void 0 : _a.nativeInstallPrompt) {
|
|
1783
|
+
this.api.pwa.nativeInstallPrompt.prompt();
|
|
1784
|
+
const choice = await this.api.pwa.nativeInstallPrompt.userChoice;
|
|
1785
1785
|
if (choice.outcome === "accepted") {
|
|
1786
1786
|
console.log("PWA installed via login link");
|
|
1787
1787
|
}
|
|
1788
|
-
this.api.pwa.
|
|
1788
|
+
this.api.pwa.nativeInstallPrompt = null;
|
|
1789
1789
|
} else {
|
|
1790
1790
|
this.api.pwa.prompt();
|
|
1791
1791
|
}
|
|
@@ -2305,12 +2305,42 @@ class Auth {
|
|
|
2305
2305
|
return this.api.request({ "$/auth/mobile/generate": { user: login, method: mode } });
|
|
2306
2306
|
}
|
|
2307
2307
|
}
|
|
2308
|
+
function createBanner(message, options = {}) {
|
|
2309
|
+
options = {
|
|
2310
|
+
color: "#dc3545",
|
|
2311
|
+
position: "top",
|
|
2312
|
+
id: "datalynk-banner",
|
|
2313
|
+
...options
|
|
2314
|
+
};
|
|
2315
|
+
removeBanner(options.id);
|
|
2316
|
+
const banner = document.createElement("div");
|
|
2317
|
+
banner.className = options.id;
|
|
2318
|
+
Object.assign(banner.style, {
|
|
2319
|
+
position: "fixed",
|
|
2320
|
+
[options.position === "top" ? "top" : "bottom"]: "0",
|
|
2321
|
+
left: "0",
|
|
2322
|
+
right: "0",
|
|
2323
|
+
padding: ".75rem",
|
|
2324
|
+
fontSize: "1rem",
|
|
2325
|
+
fontWeight: "bolder",
|
|
2326
|
+
backgroundColor: options.color,
|
|
2327
|
+
color: "#fff",
|
|
2328
|
+
textAlign: "center",
|
|
2329
|
+
zIndex: "9999"
|
|
2330
|
+
});
|
|
2331
|
+
banner.innerHTML = message;
|
|
2332
|
+
document.body.appendChild(banner);
|
|
2333
|
+
}
|
|
2334
|
+
function removeBanner(id = "datalynk-banner") {
|
|
2335
|
+
const banner = document.querySelector(`.${id}`);
|
|
2336
|
+
if (banner) banner.remove();
|
|
2337
|
+
}
|
|
2308
2338
|
class PWA {
|
|
2309
2339
|
constructor(api) {
|
|
2310
|
-
|
|
2340
|
+
/** Capture Install Event */
|
|
2341
|
+
__publicField(this, "nativeInstallPrompt", null);
|
|
2311
2342
|
this.api = api;
|
|
2312
2343
|
}
|
|
2313
|
-
// Holds the beforeinstallprompt event
|
|
2314
2344
|
get headless() {
|
|
2315
2345
|
return typeof window == "undefined";
|
|
2316
2346
|
}
|
|
@@ -2337,34 +2367,36 @@ class PWA {
|
|
|
2337
2367
|
if (!installBtn) return;
|
|
2338
2368
|
installBtn.onclick = async () => {
|
|
2339
2369
|
var _a;
|
|
2340
|
-
this.
|
|
2341
|
-
|
|
2370
|
+
if (!this.nativeInstallPrompt) return console.warn("PWA is not supported");
|
|
2371
|
+
this.nativeInstallPrompt.prompt();
|
|
2372
|
+
const choice = await this.nativeInstallPrompt.userChoice;
|
|
2342
2373
|
if (choice.outcome === "accepted") {
|
|
2343
2374
|
console.log("PWA installed");
|
|
2344
|
-
localStorage.
|
|
2375
|
+
localStorage.setItem(`${this.api.options.name}:pwa`, "installed");
|
|
2376
|
+
} else {
|
|
2377
|
+
localStorage.setItem(`${this.api.options.name}:pwa`, Date.now().toString());
|
|
2345
2378
|
}
|
|
2346
|
-
this.deferredPrompt = null;
|
|
2347
2379
|
(_a = document.querySelector(".pwa-prompt")) == null ? void 0 : _a.remove();
|
|
2348
2380
|
};
|
|
2349
2381
|
}
|
|
2350
2382
|
/** Setup the PWA */
|
|
2351
2383
|
async setup(manifest = {}) {
|
|
2352
|
-
var _a, _b, _c;
|
|
2353
2384
|
window.addEventListener("beforeinstallprompt", (e) => {
|
|
2354
2385
|
e.preventDefault();
|
|
2355
|
-
this.
|
|
2386
|
+
this.nativeInstallPrompt = e;
|
|
2356
2387
|
const actions = document.querySelector(".pwa-prompt-actions");
|
|
2357
2388
|
if (actions && !actions.querySelector("#installPwaBtn")) {
|
|
2358
2389
|
actions.innerHTML = `<button id="installPwaBtn">Install App</button>`;
|
|
2359
2390
|
this.bindInstallButton();
|
|
2360
2391
|
}
|
|
2361
2392
|
});
|
|
2362
|
-
|
|
2363
|
-
timeout:
|
|
2364
|
-
//
|
|
2365
|
-
dismissExpiry:
|
|
2366
|
-
//
|
|
2367
|
-
loginLink:
|
|
2393
|
+
this.api.options.pwaSettings = {
|
|
2394
|
+
timeout: 30,
|
|
2395
|
+
// Seconds
|
|
2396
|
+
dismissExpiry: 7,
|
|
2397
|
+
// Days
|
|
2398
|
+
loginLink: false,
|
|
2399
|
+
...this.api.options.pwaSettings
|
|
2368
2400
|
};
|
|
2369
2401
|
const meta = (key, name, value) => {
|
|
2370
2402
|
const exists = document.querySelector(`meta[${key}="${name}"]`);
|
|
@@ -2398,19 +2430,16 @@ class PWA {
|
|
|
2398
2430
|
document.head.append(link);
|
|
2399
2431
|
}
|
|
2400
2432
|
if (!this.headless && !this.iframe && !this.pwa && this.platform != "mac") setTimeout(() => {
|
|
2401
|
-
|
|
2402
|
-
if (
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2407
|
-
}
|
|
2433
|
+
var _a, _b, _c, _d;
|
|
2434
|
+
if (((_b = (_a = this.api.options) == null ? void 0 : _a.pwaSettings) == null ? void 0 : _b.dismissExpiry) === 0) localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2435
|
+
let dismissedDate = localStorage.getItem(`${this.api.options.name}:pwa`);
|
|
2436
|
+
if (dismissedDate) dismissedDate = JSON.parse(dismissedDate);
|
|
2437
|
+
if (!!dismissedDate && Date.now() > dismissedDate + ((_d = (_c = this.api.options) == null ? void 0 : _c.pwaSettings) == null ? void 0 : _d.dismissExpiry) * 6e4 * 60 * 24) return;
|
|
2408
2438
|
this.prompt();
|
|
2409
|
-
},
|
|
2439
|
+
}, this.api.options.pwaSettings.timeout);
|
|
2410
2440
|
}
|
|
2411
2441
|
/** Prompt user to install the app */
|
|
2412
2442
|
async prompt(platform) {
|
|
2413
|
-
var _a;
|
|
2414
2443
|
if (document.querySelector(".pwa-prompt")) return;
|
|
2415
2444
|
const android = (platform || this.platform) == "android";
|
|
2416
2445
|
let style = document.querySelector("style.pwa");
|
|
@@ -2505,10 +2534,10 @@ class PWA {
|
|
|
2505
2534
|
<button class="pwa-prompt-close">×</button>
|
|
2506
2535
|
</div>
|
|
2507
2536
|
<div class="pwa-prompt-body">
|
|
2508
|
-
${this.
|
|
2537
|
+
${this.nativeInstallPrompt ? "Click below to install the app directly to your device." : "Add this app to your home screen for a faster, fullscreen experience."}
|
|
2509
2538
|
</div>
|
|
2510
2539
|
<div class="pwa-prompt-actions">
|
|
2511
|
-
${this.
|
|
2540
|
+
${this.nativeInstallPrompt ? `<button id="installPwaBtn">Install App</button>` : `
|
|
2512
2541
|
<table>
|
|
2513
2542
|
<tr>
|
|
2514
2543
|
<td style="width:40px;text-align:center">
|
|
@@ -2527,13 +2556,13 @@ class PWA {
|
|
|
2527
2556
|
</div>
|
|
2528
2557
|
`;
|
|
2529
2558
|
const closeBtn = prompt.querySelector(".pwa-prompt-close");
|
|
2530
|
-
const dismissExpiry = ((_a = this.api.options.pwaSettings) == null ? void 0 : _a.dismissExpiry) ?? 7;
|
|
2531
2559
|
closeBtn.onclick = () => {
|
|
2532
|
-
|
|
2560
|
+
var _a;
|
|
2561
|
+
!!((_a = this.api.options.pwaSettings) == null ? void 0 : _a.dismissExpiry) ? localStorage.setItem(`${this.api.options.name}:pwa`, Date.now().toString()) : localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2533
2562
|
prompt.remove();
|
|
2534
2563
|
};
|
|
2535
2564
|
document.body.append(prompt);
|
|
2536
|
-
if (this.
|
|
2565
|
+
if (this.nativeInstallPrompt) this.bindInstallButton();
|
|
2537
2566
|
}
|
|
2538
2567
|
}
|
|
2539
2568
|
class Files {
|
|
@@ -3062,7 +3091,7 @@ class ApiCall {
|
|
|
3062
3091
|
return this;
|
|
3063
3092
|
}
|
|
3064
3093
|
}
|
|
3065
|
-
class
|
|
3094
|
+
const _Slice = class _Slice {
|
|
3066
3095
|
/**
|
|
3067
3096
|
* An object to aid in constructing requests
|
|
3068
3097
|
*
|
|
@@ -3073,17 +3102,20 @@ class Slice {
|
|
|
3073
3102
|
__publicField(this, "table");
|
|
3074
3103
|
__publicField(this, "info");
|
|
3075
3104
|
__publicField(this, "loaded", false);
|
|
3076
|
-
__publicField(this, "pendingInsert", 0);
|
|
3077
|
-
/** Unsubscribe from changes, undefined if not subscribed */
|
|
3078
|
-
__publicField(this, "unsubscribe");
|
|
3079
3105
|
/** Cached slice data as an observable */
|
|
3080
3106
|
__publicField(this, "cache$", new BehaviorSubject([]));
|
|
3107
|
+
/** Unsubscribe from changes, undefined if not subscribed */
|
|
3108
|
+
__publicField(this, "unsubscribe");
|
|
3081
3109
|
var _a;
|
|
3082
3110
|
this.slice = slice;
|
|
3083
3111
|
this.api = api;
|
|
3084
3112
|
if (this.offlineEnabled) {
|
|
3085
3113
|
this.table = (_a = api.database) == null ? void 0 : _a.table(slice.toString());
|
|
3086
|
-
this.table.getAll().then((resp) =>
|
|
3114
|
+
this.table.getAll().then((resp) => {
|
|
3115
|
+
this.cache = resp;
|
|
3116
|
+
const lowest = resp.reduce((acc, r) => Math.min(acc, r.id), 0);
|
|
3117
|
+
if (lowest < _Slice.nextId) _Slice.nextId = lowest;
|
|
3118
|
+
}).finally(() => this.loaded = true);
|
|
3087
3119
|
this.cache$.pipe(skip(1)).subscribe(async (cache) => {
|
|
3088
3120
|
var _a2;
|
|
3089
3121
|
await ((_a2 = this.table) == null ? void 0 : _a2.clear());
|
|
@@ -3091,10 +3123,10 @@ class Slice {
|
|
|
3091
3123
|
var _a3;
|
|
3092
3124
|
return (_a3 = this.table) == null ? void 0 : _a3.put(c.id, c);
|
|
3093
3125
|
}));
|
|
3094
|
-
this.fixIncrement();
|
|
3095
3126
|
});
|
|
3096
3127
|
this.sync();
|
|
3097
|
-
|
|
3128
|
+
this.api.online$.subscribe(async (online) => {
|
|
3129
|
+
if (!online) return;
|
|
3098
3130
|
if (this.api.expired) await lastValueFrom(this.api.auth.user$.pipe(skip(1), takeWhile((u) => !u || this.api.expired, true)));
|
|
3099
3131
|
this.pushChanges();
|
|
3100
3132
|
});
|
|
@@ -3115,10 +3147,6 @@ class Slice {
|
|
|
3115
3147
|
var _a;
|
|
3116
3148
|
return (_a = this.api.database) == null ? void 0 : _a.includes(this.slice.toString());
|
|
3117
3149
|
}
|
|
3118
|
-
fixIncrement() {
|
|
3119
|
-
var _a;
|
|
3120
|
-
this.pendingInsert = ((_a = this.cache.toSorted(sortByProp("id")).pop()) == null ? void 0 : _a["id"]) || 0;
|
|
3121
|
-
}
|
|
3122
3150
|
execWrapper(call) {
|
|
3123
3151
|
const onlineExec = call.exec.bind(call);
|
|
3124
3152
|
return async () => {
|
|
@@ -3164,7 +3192,7 @@ class Slice {
|
|
|
3164
3192
|
} else if (request["$/slice/xinsert"]) {
|
|
3165
3193
|
const rows = request["$/slice/xinsert"]["rows"].map((r) => ({
|
|
3166
3194
|
...r,
|
|
3167
|
-
id:
|
|
3195
|
+
id: --_Slice.nextId,
|
|
3168
3196
|
_sync: "insert"
|
|
3169
3197
|
}));
|
|
3170
3198
|
this.cache = [...this.cache, ...rows];
|
|
@@ -3330,7 +3358,10 @@ class Slice {
|
|
|
3330
3358
|
call.exec = this.execWrapper(call);
|
|
3331
3359
|
return call.update(rows);
|
|
3332
3360
|
}
|
|
3333
|
-
}
|
|
3361
|
+
};
|
|
3362
|
+
__publicField(_Slice, "idMap", {});
|
|
3363
|
+
__publicField(_Slice, "nextId", 0);
|
|
3364
|
+
let Slice = _Slice;
|
|
3334
3365
|
class Socket {
|
|
3335
3366
|
constructor(api, options = {}) {
|
|
3336
3367
|
__publicField(this, "listeners", []);
|
|
@@ -3465,7 +3496,7 @@ class Superuser {
|
|
|
3465
3496
|
} });
|
|
3466
3497
|
}
|
|
3467
3498
|
}
|
|
3468
|
-
const version = "1.3.
|
|
3499
|
+
const version = "1.3.7";
|
|
3469
3500
|
class WebRtc {
|
|
3470
3501
|
constructor(api) {
|
|
3471
3502
|
__publicField(this, "ice");
|
|
@@ -3639,7 +3670,7 @@ const _Api = class _Api {
|
|
|
3639
3670
|
/** Client library version */
|
|
3640
3671
|
__publicField(this, "version", version);
|
|
3641
3672
|
__publicField(this, "onlineOverride", false);
|
|
3642
|
-
__publicField(this, "
|
|
3673
|
+
__publicField(this, "online$", new BehaviorSubject(typeof navigator == "undefined" ? true : navigator.onLine));
|
|
3643
3674
|
/** API Session token */
|
|
3644
3675
|
__publicField(this, "token$", new BehaviorSubject(void 0));
|
|
3645
3676
|
var _a, _b;
|
|
@@ -3675,20 +3706,28 @@ const _Api = class _Api {
|
|
|
3675
3706
|
this.pwa = new PWA(this);
|
|
3676
3707
|
this.superuser = new Superuser(this);
|
|
3677
3708
|
this.webrtc = new WebRtc(this);
|
|
3709
|
+
this.database = new Database("datalynk", ["pending", ...this.options.offline || []]);
|
|
3710
|
+
this.online$.subscribe(async (online) => {
|
|
3711
|
+
var _a2;
|
|
3712
|
+
if (!online) return;
|
|
3713
|
+
const table = (_a2 = this.database) == null ? void 0 : _a2.table("pending");
|
|
3714
|
+
const keys = await (table == null ? void 0 : table.getAllKeys());
|
|
3715
|
+
await Promise.allSettled(keys.map(async (k) => {
|
|
3716
|
+
const r = await (table == null ? void 0 : table.get(k));
|
|
3717
|
+
await this.request(r).then(() => table == null ? void 0 : table.delete(k));
|
|
3718
|
+
}));
|
|
3719
|
+
});
|
|
3678
3720
|
if (typeof window !== "undefined") {
|
|
3679
|
-
const handleOffline = (state) => {
|
|
3680
|
-
this.
|
|
3681
|
-
this.offlineBanner();
|
|
3721
|
+
const handleOffline = (state = this.offline) => {
|
|
3722
|
+
if (this.online != state) this.online$.next(state);
|
|
3682
3723
|
};
|
|
3683
3724
|
window.addEventListener("online", () => handleOffline(true));
|
|
3684
3725
|
window.addEventListener("offline", () => handleOffline(false));
|
|
3685
|
-
this.
|
|
3686
|
-
this.offlineBanner();
|
|
3726
|
+
this.online$.subscribe(() => this.offlineBanner());
|
|
3687
3727
|
}
|
|
3688
3728
|
if ((_a = this.options.offline) == null ? void 0 : _a.length) {
|
|
3689
3729
|
this.pwa.setup();
|
|
3690
3730
|
if (typeof indexedDB == "undefined") throw new Error("Cannot enable offline support, indexedDB is not available in this environment");
|
|
3691
|
-
this.database = new Database("datalynk", this.options.offline);
|
|
3692
3731
|
(_b = this.options.offline) == null ? void 0 : _b.forEach((id) => this.slice(id));
|
|
3693
3732
|
if (this.options.serviceWorker && typeof navigator["serviceWorker"] != "undefined") {
|
|
3694
3733
|
(async () => {
|
|
@@ -3721,24 +3760,23 @@ const _Api = class _Api {
|
|
|
3721
3760
|
}
|
|
3722
3761
|
/** Check if we are connected */
|
|
3723
3762
|
get online() {
|
|
3724
|
-
return this.
|
|
3763
|
+
return this.online$.getValue();
|
|
3725
3764
|
}
|
|
3726
3765
|
get offline() {
|
|
3727
|
-
return !this.
|
|
3766
|
+
return !this.online;
|
|
3728
3767
|
}
|
|
3729
3768
|
/** Override connection status */
|
|
3730
3769
|
set online(value) {
|
|
3731
3770
|
if (value == null) {
|
|
3732
3771
|
this.onlineOverride = false;
|
|
3733
|
-
this.
|
|
3772
|
+
this.online$.next(typeof navigator == "undefined" ? true : navigator.onLine);
|
|
3734
3773
|
} else {
|
|
3735
3774
|
this.onlineOverride = true;
|
|
3736
3775
|
if (value == this.online) return;
|
|
3737
|
-
this.
|
|
3776
|
+
this.online$.next(value);
|
|
3738
3777
|
if (value) this.startHeartbeat();
|
|
3739
3778
|
else this.stopHeartbeat();
|
|
3740
3779
|
}
|
|
3741
|
-
this.offlineBanner();
|
|
3742
3780
|
}
|
|
3743
3781
|
/** Logged in spoke */
|
|
3744
3782
|
get spoke() {
|
|
@@ -3762,6 +3800,13 @@ const _Api = class _Api {
|
|
|
3762
3800
|
}),
|
|
3763
3801
|
body: JSON.stringify(_Api.translateTokens(req))
|
|
3764
3802
|
}).then(async (resp) => {
|
|
3803
|
+
const warning = resp.headers["X-Warning"];
|
|
3804
|
+
if (warning) console.warn(warning);
|
|
3805
|
+
const banner = resp.headers["X-User-Notice"];
|
|
3806
|
+
if (warning) {
|
|
3807
|
+
createBanner(banner);
|
|
3808
|
+
setTimeout(() => removeBanner(), 1e4);
|
|
3809
|
+
}
|
|
3765
3810
|
this.online = true;
|
|
3766
3811
|
let data = JSONAttemptParse(await resp.text());
|
|
3767
3812
|
if (!resp.ok || (data == null ? void 0 : data.error)) throw Object.assign(errorFromCode(resp.status, data.error), data);
|
|
@@ -3771,7 +3816,7 @@ const _Api = class _Api {
|
|
|
3771
3816
|
}
|
|
3772
3817
|
async checkConnection() {
|
|
3773
3818
|
if (typeof navigator != "undefined" && !navigator.onLine) {
|
|
3774
|
-
this.
|
|
3819
|
+
this.online$.next(false);
|
|
3775
3820
|
return;
|
|
3776
3821
|
}
|
|
3777
3822
|
if (this.onlineOverride) return;
|
|
@@ -3779,24 +3824,22 @@ const _Api = class _Api {
|
|
|
3779
3824
|
const timeout = setTimeout(() => controller.abort(), this.heartbeat.timeout);
|
|
3780
3825
|
try {
|
|
3781
3826
|
const response = await fetch(this.url + this.heartbeat.target, { signal: controller.signal });
|
|
3782
|
-
this.
|
|
3827
|
+
this.online$.next(response.ok);
|
|
3783
3828
|
} catch (error) {
|
|
3784
|
-
this.
|
|
3829
|
+
this.online$.next(false);
|
|
3785
3830
|
} finally {
|
|
3786
3831
|
clearTimeout(timeout);
|
|
3787
3832
|
}
|
|
3788
3833
|
}
|
|
3789
3834
|
offlineBanner() {
|
|
3790
3835
|
if (this.options.offlineBanner === false || typeof document == "undefined") return;
|
|
3791
|
-
const banner = document.querySelector(".datalynk-offline-banner");
|
|
3792
3836
|
if (this.online) {
|
|
3793
|
-
banner
|
|
3794
|
-
} else
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
document.body.appendChild(b);
|
|
3837
|
+
removeBanner("datalynk-offline-banner");
|
|
3838
|
+
} else {
|
|
3839
|
+
createBanner("โ ๏ธ You are offline, please reconnect to sync changes", {
|
|
3840
|
+
id: "datalynk-offline-banner",
|
|
3841
|
+
position: this.options.offlineBanner === "top" ? "top" : "bottom"
|
|
3842
|
+
});
|
|
3800
3843
|
}
|
|
3801
3844
|
}
|
|
3802
3845
|
startHeartbeat() {
|
|
@@ -3892,13 +3935,6 @@ const _Api = class _Api {
|
|
|
3892
3935
|
{ "$_": "*" }
|
|
3893
3936
|
] }, {});
|
|
3894
3937
|
}
|
|
3895
|
-
/**
|
|
3896
|
-
* Exact same as `request` method, but logs the response in the console automatically
|
|
3897
|
-
*
|
|
3898
|
-
* @param {object | string} data Datalynk request as object or string
|
|
3899
|
-
* @param {ApiRequestOptions} options
|
|
3900
|
-
* @returns {Promise<any>} Datalynk response
|
|
3901
|
-
*/
|
|
3902
3938
|
debug(data, options = {}) {
|
|
3903
3939
|
return this.request(data, options).then((data2) => {
|
|
3904
3940
|
console.log(data2);
|
|
@@ -3908,20 +3944,14 @@ const _Api = class _Api {
|
|
|
3908
3944
|
return err;
|
|
3909
3945
|
});
|
|
3910
3946
|
}
|
|
3911
|
-
/**
|
|
3912
|
-
* Send a request to Datalynk
|
|
3913
|
-
*
|
|
3914
|
-
* @example
|
|
3915
|
-
* ```ts
|
|
3916
|
-
* const response = await api.request('$/auth/current');
|
|
3917
|
-
* ```
|
|
3918
|
-
*
|
|
3919
|
-
* @param {object} data Request using Datalynk API syntax. Strings will be converted: '$/auth/current' -> {'$/auth/current': {}}
|
|
3920
|
-
* @param {ApiRequestOptions} options
|
|
3921
|
-
* @returns {Promise<any>} Datalynk response or error
|
|
3922
|
-
*/
|
|
3923
3947
|
request(data, options = {}) {
|
|
3948
|
+
var _a, _b;
|
|
3924
3949
|
data = typeof data == "string" ? { [data]: {} } : data;
|
|
3950
|
+
let key = JSON.stringify(data);
|
|
3951
|
+
if (this.offline) {
|
|
3952
|
+
(_b = (_a = this.database) == null ? void 0 : _a.table("pending")) == null ? void 0 : _b.add(data, key);
|
|
3953
|
+
return Promise.resolve();
|
|
3954
|
+
}
|
|
3925
3955
|
if (options.noOptimize) {
|
|
3926
3956
|
return new Promise((res, rej) => {
|
|
3927
3957
|
this._request(data, options).then((resp) => {
|
|
@@ -3929,7 +3959,6 @@ const _Api = class _Api {
|
|
|
3929
3959
|
}).catch((err) => rej(err));
|
|
3930
3960
|
});
|
|
3931
3961
|
}
|
|
3932
|
-
let key = JSON.stringify(data);
|
|
3933
3962
|
if (!this.pending[key]) {
|
|
3934
3963
|
this.pending[key] = new Promise((res, rej) => this.bundle.push({ data, res, rej }));
|
|
3935
3964
|
this.pending[key].catch().then(() => delete this.pending[key]);
|
package/dist/pwa.d.ts
CHANGED
package/dist/pwa.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pwa.d.ts","sourceRoot":"","sources":["../src/pwa.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAC;AAE1B,qBAAa,GAAG;
|
|
1
|
+
{"version":3,"file":"pwa.d.ts","sourceRoot":"","sources":["../src/pwa.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAC;AAE1B,qBAAa,GAAG;IAwBH,OAAO,CAAC,QAAQ,CAAC,GAAG;IAvBhC,4BAA4B;IACrB,mBAAmB,EAAE,GAAG,CAAQ;IAEvC,IAAI,QAAQ,IAAI,OAAO,CAAyC;IAEhE,IAAI,MAAM,IAAI,OAAO,CAAyC;IAE9D,IAAI,MAAM,IAAI,OAAO,CAAuD;IAE5E,IAAI,QAAQ,IAAI,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAQ1E;IAED,IAAI,GAAG,IAAI,OAAO,CAEjB;gBAE4B,GAAG,EAAE,GAAG;IAErC,OAAO,CAAC,iBAAiB;IAiBzB,oBAAoB;IACd,KAAK,CAAC,QAAQ,GAAE,GAAQ;IAqE9B,qCAAqC;IAC/B,MAAM,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,KAAK;CA6IzC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {Api} from 'https://datalynk-client.scarborough.auxilium.world/dist/index.mjs';
|
|
2
|
+
|
|
3
|
+
let api = null;
|
|
4
|
+
const CACHE_NAME = 'datalynk';
|
|
5
|
+
let ONLINE = true;
|
|
6
|
+
const netChan = 'BroadcastChannel' in self ? new BroadcastChannel('sw-net') : null;
|
|
7
|
+
|
|
8
|
+
function broadcastStatus(online) {
|
|
9
|
+
if(ONLINE === online) return;
|
|
10
|
+
ONLINE = online;
|
|
11
|
+
netChan?.postMessage({type: 'status', online, ts: Date.now()});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function getSettings() {
|
|
15
|
+
const cache = await caches.open('api-settings');
|
|
16
|
+
const response = await cache.match('/settings');
|
|
17
|
+
if(!response) return {};
|
|
18
|
+
try {
|
|
19
|
+
return (await response.clone().json()) || {};
|
|
20
|
+
} catch {
|
|
21
|
+
const txt = await response.text();
|
|
22
|
+
try { return JSON.parse(txt || '{}') || {}; }
|
|
23
|
+
catch { return null; }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function setSettings(settings) {
|
|
28
|
+
const oldSettings = await getSettings();
|
|
29
|
+
const cache = await caches.open('api-settings');
|
|
30
|
+
const newSettings = {...oldSettings, ...settings};
|
|
31
|
+
await cache.put(new Request('/settings'), new Response(JSON.stringify(newSettings), {headers: {'Content-Type': 'application/json'}}));
|
|
32
|
+
return newSettings;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
self.addEventListener('install', (event) => {
|
|
36
|
+
event.waitUntil(self.skipWaiting());
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
self.addEventListener('activate', (event) => {
|
|
40
|
+
event.waitUntil((async () => {
|
|
41
|
+
await self.clients.claim();
|
|
42
|
+
|
|
43
|
+
const keys = await caches.keys();
|
|
44
|
+
await Promise.all(
|
|
45
|
+
keys.filter((k) => k.startsWith('app-cache-') && k !== CACHE_NAME)
|
|
46
|
+
.map((k) => caches.delete(k))
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const settings = await getSettings();
|
|
50
|
+
if(settings?.url) {
|
|
51
|
+
api = new Api(settings.url, { ...(settings.options || {}), serviceWorker: false });
|
|
52
|
+
if(settings.token) api.token = settings.token;
|
|
53
|
+
}
|
|
54
|
+
})());
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
netChan?.addEventListener('message', (e) => {
|
|
58
|
+
if(e.data === 'status?') netChan?.postMessage({ type: 'status', online: ONLINE, ts: Date.now() });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
self.addEventListener('fetch', (event) => {
|
|
62
|
+
const request = event.request;
|
|
63
|
+
|
|
64
|
+
if(request.method !== 'GET') {
|
|
65
|
+
event.respondWith((async () => {
|
|
66
|
+
try {
|
|
67
|
+
const res = await fetch(request);
|
|
68
|
+
broadcastStatus(true);
|
|
69
|
+
return res;
|
|
70
|
+
} catch(err) {
|
|
71
|
+
broadcastStatus(false);
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
})());
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
event.respondWith((async () => {
|
|
79
|
+
const cache = await caches.open(CACHE_NAME);
|
|
80
|
+
if(!ONLINE) {
|
|
81
|
+
const cachedEarly = await cache.match(request);
|
|
82
|
+
if(cachedEarly) return cachedEarly;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const networkResponse = await fetch(request);
|
|
87
|
+
broadcastStatus(true);
|
|
88
|
+
if(networkResponse.status === 404) { // purge stale
|
|
89
|
+
await cache.delete(request);
|
|
90
|
+
return networkResponse;
|
|
91
|
+
}
|
|
92
|
+
const isCacheable = networkResponse.ok || networkResponse.type === 'opaque';
|
|
93
|
+
if(isCacheable) {
|
|
94
|
+
const isOpaqueNav = networkResponse.type === 'opaque' && request.mode === 'navigate';
|
|
95
|
+
if (!isOpaqueNav) {
|
|
96
|
+
event.waitUntil(cache.put(request, networkResponse.clone()));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return networkResponse;
|
|
100
|
+
} catch {
|
|
101
|
+
broadcastStatus(false);
|
|
102
|
+
const cached = await cache.match(request);
|
|
103
|
+
if(cached) return cached;
|
|
104
|
+
if(request.mode === 'navigate') {
|
|
105
|
+
const fallback = (await cache.match('/')) || (await cache.match('/index.html'));
|
|
106
|
+
if(fallback) return fallback;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return new Response('You are offline', {
|
|
110
|
+
status: 503,
|
|
111
|
+
statusText: 'Service Unavailable',
|
|
112
|
+
headers: {'Content-Type': 'text/plain'}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
})());
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
self.addEventListener('message', async (event) => {
|
|
119
|
+
if(event.data?.options) { // ๐ง Update settings
|
|
120
|
+
api = new Api(event.data.options.url, { ...event.data.options, serviceWorker: false });
|
|
121
|
+
await setSettings(event.data.options);
|
|
122
|
+
} else if(event.data?.token) { // ๐ Update token
|
|
123
|
+
if(api) api.token = event.data.token;
|
|
124
|
+
await setSettings({token: event.data.token});
|
|
125
|
+
} else if(event.data?.clearCache) { // ๐งน Clear cache (debugging)
|
|
126
|
+
await caches.delete(CACHE_NAME);
|
|
127
|
+
}
|
|
128
|
+
});
|
package/dist/slice.d.ts
CHANGED
|
@@ -231,14 +231,17 @@ export declare class ApiCall<T extends Meta = any> {
|
|
|
231
231
|
export declare class Slice<T extends Meta = any> {
|
|
232
232
|
private slice;
|
|
233
233
|
private api;
|
|
234
|
+
static idMap: {
|
|
235
|
+
[key: number]: number;
|
|
236
|
+
};
|
|
237
|
+
static nextId: number;
|
|
234
238
|
private table?;
|
|
235
239
|
private info?;
|
|
236
240
|
private loaded;
|
|
237
|
-
private pendingInsert;
|
|
238
|
-
/** Unsubscribe from changes, undefined if not subscribed */
|
|
239
|
-
unsubscribe?: Unsubscribe | null;
|
|
240
241
|
/** Cached slice data as an observable */
|
|
241
242
|
cache$: BehaviorSubject<T[]>;
|
|
243
|
+
/** Unsubscribe from changes, undefined if not subscribed */
|
|
244
|
+
unsubscribe?: Unsubscribe | null;
|
|
242
245
|
/** Cached slice data */
|
|
243
246
|
get cache(): T[];
|
|
244
247
|
/** Set cached data & alert subscribers */
|
|
@@ -252,7 +255,6 @@ export declare class Slice<T extends Meta = any> {
|
|
|
252
255
|
* @param {Api} api Api to send the requests through
|
|
253
256
|
*/
|
|
254
257
|
constructor(slice: number | string, api: Api);
|
|
255
|
-
private fixIncrement;
|
|
256
258
|
private execWrapper;
|
|
257
259
|
pushChanges(): Promise<void>;
|
|
258
260
|
/**
|
package/dist/slice.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slice.d.ts","sourceRoot":"","sources":["../src/slice.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"slice.d.ts","sourceRoot":"","sources":["../src/slice.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,eAAe,EAAiC,MAAM,MAAM,CAAC;AACrE,OAAO,EAAC,GAAG,EAAE,iBAAiB,EAAC,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAC,IAAI,EAAE,SAAS,EAAC,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAmB,WAAW,EAAC,MAAM,UAAU,CAAC;AAEvD,qBAAa,OAAO,CAAC,CAAC,SAAS,IAAI,GAAG,GAAG;IAmB5B,OAAO,CAAC,QAAQ,CAAC,GAAG;aAAuB,KAAK,EAAE,MAAM,GAAG,MAAM;IAlB7E,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,CAAgB;IACjC,OAAO,CAAC,OAAO,CAAW;IAE1B,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,0BAA0B;IAC1B,IAAI,GAAG;;;OAQN;gBAE4B,GAAG,EAAE,GAAG,EAAkB,KAAK,EAAE,MAAM,GAAG,MAAM;IAE7E;;;;;;;;;OASG;IACH,KAAK;oBA0Ga,MAAM,EAAE,GAAG,IAAI;gBACnB;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;SAAE,GAAG,IAAI;MA3GnC;IAEpB;;;;;;;;;;;OAWG;IACH,GAAG,IAAI,IAAI;IAOX;;;;;;;;;;OAUG;IACH,KAAK,CAAC,GAAG,GAAE,MAAM,GAAG,MAAa,GAAG,IAAI;IAMxC;;;;OAIG;IACH,KAAK,CAAC,OAAO,UAAO,GAAG,IAAI;IAK3B;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IASpC;;;;;;;;;;OAUG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK5B;;;;OAIG;IACH,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,CAAC;IAStD;;;;;;;;;OASG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IACjC,MAAM,CAAC,KAAK,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI;IAQvD;;;OAGG;IACH,EAAE,IAAI,IAAI;IAIV;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI;IAQ3B;;;;;;;;;OASG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAKxB;;;;;;;;;;;;;OAaG;IACH,EAAE;IAUF;;;;;;;;;;OAUG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,UAAO,GAAG,IAAI;IAM5C;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAK/B;;;OAGG;IACH,GAAG,IAAI,IAAI;IAIX;;;OAGG;IACH,IAAI,IAAI,IAAI;IAIZ;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE;IAMlB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IASpC;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI;IAQ3B;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,GAAG,IAAI;CAiCnE;AAED;;GAEG;AACH,qBAAa,KAAK,CAAC,CAAC,SAAS,IAAI,GAAG,GAAG;IA4B1B,OAAO,CAAC,KAAK;IAAmB,OAAO,CAAC,GAAG;IA3BvD,MAAM,CAAC,KAAK,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAM;IAC3C,MAAM,CAAC,MAAM,SAAK;IAElB,OAAO,CAAC,KAAK,CAAC,CAAmB;IACjC,OAAO,CAAC,IAAI,CAAC,CAAY;IACzB,OAAO,CAAC,MAAM,CAAS;IAEvB,yCAAyC;IACzC,MAAM,uBAAgC;IACtC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAEjC,wBAAwB;IACxB,IAAI,KAAK,IAAI,CAAC,EAAE,CAAmC;IAEnD,0CAA0C;IAC1C,OAAO,KAAK,KAAK,QAAyC;IAE1D,uCAAuC;IACvC,IAAI,cAAc,wBAAiE;IAEnF;;;;;OAKG;gBACiB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAU,GAAG,EAAE,GAAG;IA8B5D,OAAO,CAAC,WAAW;IAqFb,WAAW;IAoCjB;;;;OAIG;IACG,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC;IA8BnD;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,UAAO;IAyBd;;OAEG;IACH,KAAK,CAAC,GAAG,GAAE,MAAM,GAAG,MAAa;IAMjC;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE;IAM5B;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE;IAMpB;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE;IAMlB;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAO7B;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE;CAKpB"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@auxilium/datalynk-client",
|
|
3
3
|
"description": "Datalynk client library",
|
|
4
4
|
"repository": "https://gitlab.auxiliumgroup.com/auxilium/datalynk/datalynk-client",
|
|
5
|
-
"version": "1.3.
|
|
5
|
+
"version": "1.3.7",
|
|
6
6
|
"author": "Zak Timson <zaktimson@gmail.com>",
|
|
7
7
|
"private": false,
|
|
8
8
|
"main": "./dist/index.cjs",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "tsc && npx vite build",
|
|
23
|
+
"postbuild": "node -e \"const fs=require('fs');fs.cpSync('src/service.worker.mjs','dist/service.worker.mjs')\"",
|
|
23
24
|
"datalynk-models": "node --no-warnings ./bin/datalynk-models.mjs",
|
|
24
25
|
"docs": "typedoc --cleanOutputDir false --out ./docs --entryPoints src/**/*.ts --readme README.md",
|
|
25
26
|
"watch": "vite build --watch"
|