@auxilium/datalynk-client 1.6.0 โ†’ 1.6.2

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 CHANGED
@@ -289,7 +289,8 @@ api.pwa.prompt();
289
289
 
290
290
  #### Static assets
291
291
  Upload the [service worker](https://datalynk-client.primary.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
292
+ to cache app files for use offline. Replace `/service.worker.mjs` on every client release; updating the library alone does not replace the server's copy.
293
+ Serve the worker with `Cache-Control: no-cache` so browsers can detect changes. The client checks for updates on startup and when it comes back online. A device must reconnect to receive a new worker.
293
294
  ```ts
294
295
  const api = new Api(`https://${spoke}.auxiliumgroup.com`, {
295
296
  serviceWorker: 'https://.../service.worker.mjs', // Shouldnt be needed but can be overriden
package/dist/api.d.ts CHANGED
@@ -57,6 +57,8 @@ export type ApiOptions = {
57
57
  watchTokenExpiry?: boolean;
58
58
  /** Service worker URL */
59
59
  serviceWorker?: string;
60
+ /** Extra app files to save before they are opened offline. */
61
+ offlineAssets?: string[];
60
62
  /** Disable sockets with false or override socket URL */
61
63
  socket?: false | string;
62
64
  /** Socket authentication mode. Existing clients use token authentication unless ticket mode is explicitly enabled. */
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/dist/auth.d.ts CHANGED
@@ -64,6 +64,8 @@ export type User = {
64
64
  export declare class Auth {
65
65
  private readonly api;
66
66
  private onlinePrompt?;
67
+ private offlineUnlocked;
68
+ private wasOffline;
67
69
  /** Current user as an observable */
68
70
  user$: BehaviorSubject<User | null | undefined>;
69
71
  /** Current user */
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1536,13 +1536,81 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1536
1536
  `;
1537
1537
  });
1538
1538
  let LoginPrompt = _LoginPrompt;
1539
+ const KEY = "datalynk-offline-login";
1540
+ const ITERATIONS = 31e4;
1541
+ async function hash(password, salt) {
1542
+ const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveBits"]);
1543
+ return Array.from(new Uint8Array(await crypto.subtle.deriveBits(
1544
+ { name: "PBKDF2", salt: new Uint8Array(salt), iterations: ITERATIONS, hash: "SHA-256" },
1545
+ key,
1546
+ 256
1547
+ )));
1548
+ }
1549
+ async function rememberOfflineLogin(realm, login, uid, password) {
1550
+ const salt = crypto.getRandomValues(new Uint8Array(16));
1551
+ const record = { realm, login: login.trim().toLowerCase(), uid, salt: Array.from(salt), hash: await hash(password, salt) };
1552
+ localStorage.setItem(KEY, JSON.stringify(record));
1553
+ }
1554
+ async function verifyOfflineLogin(realm, login, uid, password) {
1555
+ const saved = localStorage.getItem(KEY);
1556
+ if (!saved) return false;
1557
+ let record;
1558
+ try {
1559
+ record = JSON.parse(saved);
1560
+ } catch {
1561
+ return false;
1562
+ }
1563
+ if (record.realm !== realm || record.login !== login.trim().toLowerCase() || record.uid !== uid || !Array.isArray(record.salt) || !Array.isArray(record.hash)) return false;
1564
+ const candidate = await hash(password, new Uint8Array(record.salt));
1565
+ if (candidate.length !== record.hash.length) return false;
1566
+ let difference = 0;
1567
+ for (let i = 0; i < candidate.length; i++) difference |= candidate[i] ^ record.hash[i];
1568
+ return difference === 0;
1569
+ }
1570
+ function hasOfflineLogin(realm, uid) {
1571
+ try {
1572
+ const record = JSON.parse(localStorage.getItem(KEY) || "null");
1573
+ return (record == null ? void 0 : record.realm) === realm && (record == null ? void 0 : record.uid) === uid;
1574
+ } catch {
1575
+ return false;
1576
+ }
1577
+ }
1578
+ function forgetOfflineLogin() {
1579
+ localStorage.removeItem(KEY);
1580
+ }
1539
1581
  class Auth {
1540
1582
  constructor(api) {
1541
1583
  __publicField(this, "onlinePrompt");
1584
+ __publicField(this, "offlineUnlocked", false);
1585
+ __publicField(this, "wasOffline", false);
1542
1586
  /** Current user as an observable */
1543
1587
  __publicField(this, "user$", new BehaviorSubject(void 0));
1544
1588
  var _a;
1545
1589
  this.api = api;
1590
+ this.api.status$.subscribe(async (status) => {
1591
+ if (status === "offline" || status === "unavailable") this.wasOffline = true;
1592
+ if (status !== "online" || !this.wasOffline) return;
1593
+ this.wasOffline = false;
1594
+ const wasUnlocked = this.offlineUnlocked;
1595
+ const previousUser = this.user;
1596
+ this.offlineUnlocked = false;
1597
+ this.user = void 0;
1598
+ try {
1599
+ const user = await this.current();
1600
+ const identity2 = this.api.jwtPayload;
1601
+ if (!user || user.guest || !identity2 || !hasOfflineLogin(identity2.realm, identity2.uid))
1602
+ forgetOfflineLogin();
1603
+ this.user = user;
1604
+ } catch (error) {
1605
+ if (this.api.status === "unauthorized") forgetOfflineLogin();
1606
+ else {
1607
+ this.wasOffline = true;
1608
+ this.offlineUnlocked = wasUnlocked;
1609
+ this.user = previousUser;
1610
+ }
1611
+ console.error("Unable to recheck the Datalynk user", error);
1612
+ }
1613
+ });
1546
1614
  this.api.token$.subscribe(async (token) => {
1547
1615
  if (token === void 0) return;
1548
1616
  try {
@@ -1554,8 +1622,9 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1554
1622
  });
1555
1623
  if ((_a = this.api.options.offline) == null ? void 0 : _a.length)
1556
1624
  this.user$.pipe(filter((u) => u !== void 0)).subscribe((u) => {
1557
- if (u == null) localStorage.removeItem("datalynk-user");
1558
- else localStorage.setItem("datalynk-user", JSON.stringify(u));
1625
+ if (u == null) {
1626
+ if (this.api.online) localStorage.removeItem("datalynk-user");
1627
+ } else localStorage.setItem("datalynk-user", JSON.stringify(u));
1559
1628
  });
1560
1629
  }
1561
1630
  /** Current user */
@@ -1579,8 +1648,9 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1579
1648
  var _a;
1580
1649
  if (!token) return null;
1581
1650
  else if (token == ((_a = this.user) == null ? void 0 : _a.token)) return this.user;
1582
- else if (!this.api.online && typeof localStorage != "undefined" && localStorage.getItem("datalynk-user"))
1651
+ else if (!this.api.online && this.offlineUnlocked && typeof localStorage != "undefined" && localStorage.getItem("datalynk-user"))
1583
1652
  return JSON.parse(localStorage.getItem("datalynk-user"));
1653
+ else if (!this.api.online) return null;
1584
1654
  return this.api.request([{ "$/auth/current": {} }, { "$/env/me": {} }], { token }).then((resp) => resp[0] || resp[1] ? { ...resp[0], ...resp[1] } : null);
1585
1655
  }
1586
1656
  /**
@@ -1592,7 +1662,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1592
1662
  * @return {Promise<void>} Login complete
1593
1663
  */
1594
1664
  async handleLogin(spoke, options) {
1595
- var _a, _b, _c;
1665
+ var _a, _b, _c, _d;
1596
1666
  if (this.onlinePrompt) window.removeEventListener("online", this.onlinePrompt);
1597
1667
  if ((_a = this.api.options.offline) == null ? void 0 : _a.length) window.removeEventListener("online", this.onlinePrompt = () => this.handleLogin(spoke, options));
1598
1668
  const urlParams = new URLSearchParams(location.search);
@@ -1601,10 +1671,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1601
1671
  this.api.token = urlToken;
1602
1672
  urlParams.delete("datalynkToken");
1603
1673
  location.href = location.pathname + (urlParams.toString() ? "?" + urlParams.toString() : "") + location.hash;
1604
- } else if (this.api.reauthenticationPending && !this.api.online && ((_b = this.api.jwtPayload) == null ? void 0 : _b.realm) == spoke) {
1674
+ } else if (!this.api.online && ((_b = this.api.options.offline) == null ? void 0 : _b.length) && !this.offlineUnlocked) {
1675
+ await this.loginPrompt(spoke, options).done;
1676
+ return;
1677
+ } else if (this.api.reauthenticationPending && !this.api.online && ((_c = this.api.jwtPayload) == null ? void 0 : _c.realm) == spoke) {
1605
1678
  return;
1606
1679
  } else if (this.api.token && !this.api.expired) {
1607
- if (((_c = this.api.jwtPayload) == null ? void 0 : _c.realm) != spoke) {
1680
+ if (((_d = this.api.jwtPayload) == null ? void 0 : _d.realm) != spoke) {
1608
1681
  this.api.token = null;
1609
1682
  location.reload();
1610
1683
  }
@@ -1664,10 +1737,23 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1664
1737
  * @param {twoFactor?: string, expire?: string} opts 2FA code & expire date (YYYY-MM-DD)
1665
1738
  * @returns {Promise<User>} User account
1666
1739
  */
1667
- login(spoke, login, password, opts) {
1740
+ async login(spoke, login, password, opts) {
1741
+ var _a, _b, _c;
1742
+ if (((_a = this.api.options.offline) == null ? void 0 : _a.length) && (!this.api.online || typeof navigator !== "undefined" && !navigator.onLine)) {
1743
+ const identity2 = this.api.jwtPayload;
1744
+ const saved = localStorage.getItem("datalynk-user");
1745
+ if (!identity2 || identity2.realm !== spoke || !saved || !await verifyOfflineLogin(spoke, login, identity2.uid, password.trim()))
1746
+ throw new Error("Offline login is not available for this account on this device.");
1747
+ const user = JSON.parse(saved);
1748
+ if (![user.login, user.email].some((value) => (value == null ? void 0 : value.toLowerCase()) === login.trim().toLowerCase()))
1749
+ throw new Error("Offline login is not available for this account on this device.");
1750
+ this.offlineUnlocked = true;
1751
+ this.user = user;
1752
+ return user;
1753
+ }
1668
1754
  const date = /* @__PURE__ */ new Date();
1669
1755
  date.setFullYear(date.getFullYear() + 1);
1670
- return fetch(`${this.api.url}login`, {
1756
+ const resp = await fetch(`${this.api.url}login`, {
1671
1757
  method: "POST",
1672
1758
  headers: { "Content-Type": "application/json" },
1673
1759
  body: JSON.stringify(clean({
@@ -1678,12 +1764,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1678
1764
  expireAt: (opts == null ? void 0 : opts.expire) == null ? `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}` : opts == null ? void 0 : opts.expire,
1679
1765
  dateFormat: "ISO8601"
1680
1766
  }))
1681
- }).then(async (resp) => {
1682
- const data = await resp.json().catch(() => ({}));
1683
- if (!resp.ok || data["error"]) throw Object.assign(errorFromCode(resp.status, data.error) || {}, data);
1684
- this.api.token = data["token"];
1685
- return data;
1686
1767
  });
1768
+ const data = await resp.json().catch(() => ({}));
1769
+ if (!resp.ok || data["error"]) throw Object.assign(errorFromCode(resp.status, data.error) || {}, data);
1770
+ this.api.token = data["token"];
1771
+ if (((_b = this.api.options.offline) == null ? void 0 : _b.length) && this.api.options.saveSession && ((_c = this.api.jwtPayload) == null ? void 0 : _c.uid) != null)
1772
+ await rememberOfflineLogin(spoke, login, this.api.jwtPayload.uid, password.trim());
1773
+ return data;
1687
1774
  }
1688
1775
  /**
1689
1776
  * Login as guest user
@@ -1715,6 +1802,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1715
1802
  */
1716
1803
  logout(reload = true) {
1717
1804
  localStorage.removeItem("datalynk-user");
1805
+ forgetOfflineLogin();
1718
1806
  return this.api.request({ "$/auth/logout": {} }).then((resp) => {
1719
1807
  if (reload) location.reload();
1720
1808
  this.api.token = null;
@@ -3322,7 +3410,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3322
3410
  } });
3323
3411
  }
3324
3412
  }
3325
- const version = "1.6.0";
3413
+ const version = "1.6.2";
3326
3414
  class WebRtc {
3327
3415
  constructor(api) {
3328
3416
  __publicField(this, "ice");
@@ -4007,18 +4095,40 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4007
4095
  (_b = this.options.offline) == null ? void 0 : _b.forEach((id) => this.slice(id));
4008
4096
  if (this.options.serviceWorker && typeof navigator["serviceWorker"] != "undefined") {
4009
4097
  (async () => {
4098
+ let initialized = false;
4099
+ const sendAssets = () => {
4100
+ var _a2;
4101
+ const assets = [
4102
+ location.pathname,
4103
+ ...Array.from(document.querySelectorAll('script[src], link[rel="stylesheet"][href], link[rel="icon"][href], img[src]')).map((element) => element.getAttribute("src") || element.getAttribute("href") || ""),
4104
+ ...performance.getEntriesByType("resource").filter((entry) => ["script", "link", "css", "img", "font"].includes(entry.initiatorType)).map((entry) => entry.name),
4105
+ ...this.options.offlineAssets || []
4106
+ ].filter(Boolean).map((value) => new URL(value, location.href).href);
4107
+ (_a2 = navigator.serviceWorker.controller) == null ? void 0 : _a2.postMessage({ offlineAssets: assets });
4108
+ };
4010
4109
  const init = () => {
4011
4110
  var _a2;
4012
4111
  (_a2 = navigator.serviceWorker.controller) == null ? void 0 : _a2.postMessage({ options: { ...this.options, url: this.url } });
4013
- this.token$.subscribe((token) => {
4014
- var _a3;
4015
- return (_a3 = navigator.serviceWorker.controller) == null ? void 0 : _a3.postMessage({ token });
4016
- });
4112
+ if (!initialized) {
4113
+ initialized = true;
4114
+ this.token$.subscribe((token) => {
4115
+ var _a3;
4116
+ return (_a3 = navigator.serviceWorker.controller) == null ? void 0 : _a3.postMessage({ token });
4117
+ });
4118
+ }
4119
+ sendAssets();
4017
4120
  };
4018
- await navigator.serviceWorker.getRegistration(this.options.serviceWorker).then((reg) => reg ?? navigator.serviceWorker.register(this.options.serviceWorker, { scope: "/", type: "module" }));
4121
+ const registration = await navigator.serviceWorker.getRegistration(this.options.serviceWorker).then((reg) => reg ?? navigator.serviceWorker.register(this.options.serviceWorker, { scope: "/", type: "module" }));
4122
+ navigator.serviceWorker.addEventListener("controllerchange", () => {
4123
+ if (navigator.serviceWorker.controller) init();
4124
+ });
4019
4125
  navigator.serviceWorker.ready.then(() => {
4020
- if (navigator.serviceWorker.controller) navigator.serviceWorker.ready.then(() => init());
4021
- else navigator.serviceWorker.addEventListener("controllerchange", () => init(), { once: true });
4126
+ if (navigator.serviceWorker.controller) init();
4127
+ });
4128
+ registration.update().catch((error) => console.warn("Service worker update check failed", error));
4129
+ window.addEventListener("online", () => {
4130
+ registration.update().catch((error) => console.warn("Service worker update check failed", error));
4131
+ if (navigator.serviceWorker.controller) sendAssets();
4022
4132
  });
4023
4133
  })();
4024
4134
  }
@@ -4548,6 +4658,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4548
4658
  async finishUnauthorizedLogout(preservePending = false) {
4549
4659
  var _a;
4550
4660
  if (this.options.manageSession === false) return;
4661
+ forgetOfflineLogin();
4551
4662
  if (typeof localStorage != "undefined") {
4552
4663
  localStorage.removeItem(this.localStorageKey);
4553
4664
  localStorage.removeItem("datalynk-user");
@@ -4563,13 +4674,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4563
4674
  await this.clearOfflineSessionCache(preservePending);
4564
4675
  if (typeof caches != "undefined") {
4565
4676
  try {
4566
- await Promise.all([caches.delete("datalynk"), caches.delete("api-settings")]);
4677
+ await caches.delete("api-settings");
4567
4678
  } catch (error) {
4568
4679
  console.error("Unable to clear Datalynk service worker cache", error);
4569
4680
  }
4570
4681
  }
4571
4682
  if (typeof navigator != "undefined" && ((_a = navigator.serviceWorker) == null ? void 0 : _a.controller)) {
4572
- navigator.serviceWorker.controller.postMessage({ token: null, clearCache: true });
4683
+ navigator.serviceWorker.controller.postMessage({ token: null });
4573
4684
  }
4574
4685
  if (typeof location != "undefined" && typeof location.reload == "function")
4575
4686
  location.reload();
package/dist/index.mjs CHANGED
@@ -1532,13 +1532,81 @@ __publicField(_LoginPrompt, "template", (options) => {
1532
1532
  `;
1533
1533
  });
1534
1534
  let LoginPrompt = _LoginPrompt;
1535
+ const KEY = "datalynk-offline-login";
1536
+ const ITERATIONS = 31e4;
1537
+ async function hash(password, salt) {
1538
+ const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveBits"]);
1539
+ return Array.from(new Uint8Array(await crypto.subtle.deriveBits(
1540
+ { name: "PBKDF2", salt: new Uint8Array(salt), iterations: ITERATIONS, hash: "SHA-256" },
1541
+ key,
1542
+ 256
1543
+ )));
1544
+ }
1545
+ async function rememberOfflineLogin(realm, login, uid, password) {
1546
+ const salt = crypto.getRandomValues(new Uint8Array(16));
1547
+ const record = { realm, login: login.trim().toLowerCase(), uid, salt: Array.from(salt), hash: await hash(password, salt) };
1548
+ localStorage.setItem(KEY, JSON.stringify(record));
1549
+ }
1550
+ async function verifyOfflineLogin(realm, login, uid, password) {
1551
+ const saved = localStorage.getItem(KEY);
1552
+ if (!saved) return false;
1553
+ let record;
1554
+ try {
1555
+ record = JSON.parse(saved);
1556
+ } catch {
1557
+ return false;
1558
+ }
1559
+ if (record.realm !== realm || record.login !== login.trim().toLowerCase() || record.uid !== uid || !Array.isArray(record.salt) || !Array.isArray(record.hash)) return false;
1560
+ const candidate = await hash(password, new Uint8Array(record.salt));
1561
+ if (candidate.length !== record.hash.length) return false;
1562
+ let difference = 0;
1563
+ for (let i = 0; i < candidate.length; i++) difference |= candidate[i] ^ record.hash[i];
1564
+ return difference === 0;
1565
+ }
1566
+ function hasOfflineLogin(realm, uid) {
1567
+ try {
1568
+ const record = JSON.parse(localStorage.getItem(KEY) || "null");
1569
+ return (record == null ? void 0 : record.realm) === realm && (record == null ? void 0 : record.uid) === uid;
1570
+ } catch {
1571
+ return false;
1572
+ }
1573
+ }
1574
+ function forgetOfflineLogin() {
1575
+ localStorage.removeItem(KEY);
1576
+ }
1535
1577
  class Auth {
1536
1578
  constructor(api) {
1537
1579
  __publicField(this, "onlinePrompt");
1580
+ __publicField(this, "offlineUnlocked", false);
1581
+ __publicField(this, "wasOffline", false);
1538
1582
  /** Current user as an observable */
1539
1583
  __publicField(this, "user$", new BehaviorSubject(void 0));
1540
1584
  var _a;
1541
1585
  this.api = api;
1586
+ this.api.status$.subscribe(async (status) => {
1587
+ if (status === "offline" || status === "unavailable") this.wasOffline = true;
1588
+ if (status !== "online" || !this.wasOffline) return;
1589
+ this.wasOffline = false;
1590
+ const wasUnlocked = this.offlineUnlocked;
1591
+ const previousUser = this.user;
1592
+ this.offlineUnlocked = false;
1593
+ this.user = void 0;
1594
+ try {
1595
+ const user = await this.current();
1596
+ const identity2 = this.api.jwtPayload;
1597
+ if (!user || user.guest || !identity2 || !hasOfflineLogin(identity2.realm, identity2.uid))
1598
+ forgetOfflineLogin();
1599
+ this.user = user;
1600
+ } catch (error) {
1601
+ if (this.api.status === "unauthorized") forgetOfflineLogin();
1602
+ else {
1603
+ this.wasOffline = true;
1604
+ this.offlineUnlocked = wasUnlocked;
1605
+ this.user = previousUser;
1606
+ }
1607
+ console.error("Unable to recheck the Datalynk user", error);
1608
+ }
1609
+ });
1542
1610
  this.api.token$.subscribe(async (token) => {
1543
1611
  if (token === void 0) return;
1544
1612
  try {
@@ -1550,8 +1618,9 @@ class Auth {
1550
1618
  });
1551
1619
  if ((_a = this.api.options.offline) == null ? void 0 : _a.length)
1552
1620
  this.user$.pipe(filter((u) => u !== void 0)).subscribe((u) => {
1553
- if (u == null) localStorage.removeItem("datalynk-user");
1554
- else localStorage.setItem("datalynk-user", JSON.stringify(u));
1621
+ if (u == null) {
1622
+ if (this.api.online) localStorage.removeItem("datalynk-user");
1623
+ } else localStorage.setItem("datalynk-user", JSON.stringify(u));
1555
1624
  });
1556
1625
  }
1557
1626
  /** Current user */
@@ -1575,8 +1644,9 @@ class Auth {
1575
1644
  var _a;
1576
1645
  if (!token) return null;
1577
1646
  else if (token == ((_a = this.user) == null ? void 0 : _a.token)) return this.user;
1578
- else if (!this.api.online && typeof localStorage != "undefined" && localStorage.getItem("datalynk-user"))
1647
+ else if (!this.api.online && this.offlineUnlocked && typeof localStorage != "undefined" && localStorage.getItem("datalynk-user"))
1579
1648
  return JSON.parse(localStorage.getItem("datalynk-user"));
1649
+ else if (!this.api.online) return null;
1580
1650
  return this.api.request([{ "$/auth/current": {} }, { "$/env/me": {} }], { token }).then((resp) => resp[0] || resp[1] ? { ...resp[0], ...resp[1] } : null);
1581
1651
  }
1582
1652
  /**
@@ -1588,7 +1658,7 @@ class Auth {
1588
1658
  * @return {Promise<void>} Login complete
1589
1659
  */
1590
1660
  async handleLogin(spoke, options) {
1591
- var _a, _b, _c;
1661
+ var _a, _b, _c, _d;
1592
1662
  if (this.onlinePrompt) window.removeEventListener("online", this.onlinePrompt);
1593
1663
  if ((_a = this.api.options.offline) == null ? void 0 : _a.length) window.removeEventListener("online", this.onlinePrompt = () => this.handleLogin(spoke, options));
1594
1664
  const urlParams = new URLSearchParams(location.search);
@@ -1597,10 +1667,13 @@ class Auth {
1597
1667
  this.api.token = urlToken;
1598
1668
  urlParams.delete("datalynkToken");
1599
1669
  location.href = location.pathname + (urlParams.toString() ? "?" + urlParams.toString() : "") + location.hash;
1600
- } else if (this.api.reauthenticationPending && !this.api.online && ((_b = this.api.jwtPayload) == null ? void 0 : _b.realm) == spoke) {
1670
+ } else if (!this.api.online && ((_b = this.api.options.offline) == null ? void 0 : _b.length) && !this.offlineUnlocked) {
1671
+ await this.loginPrompt(spoke, options).done;
1672
+ return;
1673
+ } else if (this.api.reauthenticationPending && !this.api.online && ((_c = this.api.jwtPayload) == null ? void 0 : _c.realm) == spoke) {
1601
1674
  return;
1602
1675
  } else if (this.api.token && !this.api.expired) {
1603
- if (((_c = this.api.jwtPayload) == null ? void 0 : _c.realm) != spoke) {
1676
+ if (((_d = this.api.jwtPayload) == null ? void 0 : _d.realm) != spoke) {
1604
1677
  this.api.token = null;
1605
1678
  location.reload();
1606
1679
  }
@@ -1660,10 +1733,23 @@ class Auth {
1660
1733
  * @param {twoFactor?: string, expire?: string} opts 2FA code & expire date (YYYY-MM-DD)
1661
1734
  * @returns {Promise<User>} User account
1662
1735
  */
1663
- login(spoke, login, password, opts) {
1736
+ async login(spoke, login, password, opts) {
1737
+ var _a, _b, _c;
1738
+ if (((_a = this.api.options.offline) == null ? void 0 : _a.length) && (!this.api.online || typeof navigator !== "undefined" && !navigator.onLine)) {
1739
+ const identity2 = this.api.jwtPayload;
1740
+ const saved = localStorage.getItem("datalynk-user");
1741
+ if (!identity2 || identity2.realm !== spoke || !saved || !await verifyOfflineLogin(spoke, login, identity2.uid, password.trim()))
1742
+ throw new Error("Offline login is not available for this account on this device.");
1743
+ const user = JSON.parse(saved);
1744
+ if (![user.login, user.email].some((value) => (value == null ? void 0 : value.toLowerCase()) === login.trim().toLowerCase()))
1745
+ throw new Error("Offline login is not available for this account on this device.");
1746
+ this.offlineUnlocked = true;
1747
+ this.user = user;
1748
+ return user;
1749
+ }
1664
1750
  const date = /* @__PURE__ */ new Date();
1665
1751
  date.setFullYear(date.getFullYear() + 1);
1666
- return fetch(`${this.api.url}login`, {
1752
+ const resp = await fetch(`${this.api.url}login`, {
1667
1753
  method: "POST",
1668
1754
  headers: { "Content-Type": "application/json" },
1669
1755
  body: JSON.stringify(clean({
@@ -1674,12 +1760,13 @@ class Auth {
1674
1760
  expireAt: (opts == null ? void 0 : opts.expire) == null ? `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}` : opts == null ? void 0 : opts.expire,
1675
1761
  dateFormat: "ISO8601"
1676
1762
  }))
1677
- }).then(async (resp) => {
1678
- const data = await resp.json().catch(() => ({}));
1679
- if (!resp.ok || data["error"]) throw Object.assign(errorFromCode(resp.status, data.error) || {}, data);
1680
- this.api.token = data["token"];
1681
- return data;
1682
1763
  });
1764
+ const data = await resp.json().catch(() => ({}));
1765
+ if (!resp.ok || data["error"]) throw Object.assign(errorFromCode(resp.status, data.error) || {}, data);
1766
+ this.api.token = data["token"];
1767
+ if (((_b = this.api.options.offline) == null ? void 0 : _b.length) && this.api.options.saveSession && ((_c = this.api.jwtPayload) == null ? void 0 : _c.uid) != null)
1768
+ await rememberOfflineLogin(spoke, login, this.api.jwtPayload.uid, password.trim());
1769
+ return data;
1683
1770
  }
1684
1771
  /**
1685
1772
  * Login as guest user
@@ -1711,6 +1798,7 @@ class Auth {
1711
1798
  */
1712
1799
  logout(reload = true) {
1713
1800
  localStorage.removeItem("datalynk-user");
1801
+ forgetOfflineLogin();
1714
1802
  return this.api.request({ "$/auth/logout": {} }).then((resp) => {
1715
1803
  if (reload) location.reload();
1716
1804
  this.api.token = null;
@@ -3318,7 +3406,7 @@ class Superuser {
3318
3406
  } });
3319
3407
  }
3320
3408
  }
3321
- const version = "1.6.0";
3409
+ const version = "1.6.2";
3322
3410
  class WebRtc {
3323
3411
  constructor(api) {
3324
3412
  __publicField(this, "ice");
@@ -4003,18 +4091,40 @@ const _Api = class _Api {
4003
4091
  (_b = this.options.offline) == null ? void 0 : _b.forEach((id) => this.slice(id));
4004
4092
  if (this.options.serviceWorker && typeof navigator["serviceWorker"] != "undefined") {
4005
4093
  (async () => {
4094
+ let initialized = false;
4095
+ const sendAssets = () => {
4096
+ var _a2;
4097
+ const assets = [
4098
+ location.pathname,
4099
+ ...Array.from(document.querySelectorAll('script[src], link[rel="stylesheet"][href], link[rel="icon"][href], img[src]')).map((element) => element.getAttribute("src") || element.getAttribute("href") || ""),
4100
+ ...performance.getEntriesByType("resource").filter((entry) => ["script", "link", "css", "img", "font"].includes(entry.initiatorType)).map((entry) => entry.name),
4101
+ ...this.options.offlineAssets || []
4102
+ ].filter(Boolean).map((value) => new URL(value, location.href).href);
4103
+ (_a2 = navigator.serviceWorker.controller) == null ? void 0 : _a2.postMessage({ offlineAssets: assets });
4104
+ };
4006
4105
  const init = () => {
4007
4106
  var _a2;
4008
4107
  (_a2 = navigator.serviceWorker.controller) == null ? void 0 : _a2.postMessage({ options: { ...this.options, url: this.url } });
4009
- this.token$.subscribe((token) => {
4010
- var _a3;
4011
- return (_a3 = navigator.serviceWorker.controller) == null ? void 0 : _a3.postMessage({ token });
4012
- });
4108
+ if (!initialized) {
4109
+ initialized = true;
4110
+ this.token$.subscribe((token) => {
4111
+ var _a3;
4112
+ return (_a3 = navigator.serviceWorker.controller) == null ? void 0 : _a3.postMessage({ token });
4113
+ });
4114
+ }
4115
+ sendAssets();
4013
4116
  };
4014
- await navigator.serviceWorker.getRegistration(this.options.serviceWorker).then((reg) => reg ?? navigator.serviceWorker.register(this.options.serviceWorker, { scope: "/", type: "module" }));
4117
+ const registration = await navigator.serviceWorker.getRegistration(this.options.serviceWorker).then((reg) => reg ?? navigator.serviceWorker.register(this.options.serviceWorker, { scope: "/", type: "module" }));
4118
+ navigator.serviceWorker.addEventListener("controllerchange", () => {
4119
+ if (navigator.serviceWorker.controller) init();
4120
+ });
4015
4121
  navigator.serviceWorker.ready.then(() => {
4016
- if (navigator.serviceWorker.controller) navigator.serviceWorker.ready.then(() => init());
4017
- else navigator.serviceWorker.addEventListener("controllerchange", () => init(), { once: true });
4122
+ if (navigator.serviceWorker.controller) init();
4123
+ });
4124
+ registration.update().catch((error) => console.warn("Service worker update check failed", error));
4125
+ window.addEventListener("online", () => {
4126
+ registration.update().catch((error) => console.warn("Service worker update check failed", error));
4127
+ if (navigator.serviceWorker.controller) sendAssets();
4018
4128
  });
4019
4129
  })();
4020
4130
  }
@@ -4544,6 +4654,7 @@ const _Api = class _Api {
4544
4654
  async finishUnauthorizedLogout(preservePending = false) {
4545
4655
  var _a;
4546
4656
  if (this.options.manageSession === false) return;
4657
+ forgetOfflineLogin();
4547
4658
  if (typeof localStorage != "undefined") {
4548
4659
  localStorage.removeItem(this.localStorageKey);
4549
4660
  localStorage.removeItem("datalynk-user");
@@ -4559,13 +4670,13 @@ const _Api = class _Api {
4559
4670
  await this.clearOfflineSessionCache(preservePending);
4560
4671
  if (typeof caches != "undefined") {
4561
4672
  try {
4562
- await Promise.all([caches.delete("datalynk"), caches.delete("api-settings")]);
4673
+ await caches.delete("api-settings");
4563
4674
  } catch (error) {
4564
4675
  console.error("Unable to clear Datalynk service worker cache", error);
4565
4676
  }
4566
4677
  }
4567
4678
  if (typeof navigator != "undefined" && ((_a = navigator.serviceWorker) == null ? void 0 : _a.controller)) {
4568
- navigator.serviceWorker.controller.postMessage({ token: null, clearCache: true });
4679
+ navigator.serviceWorker.controller.postMessage({ token: null });
4569
4680
  }
4570
4681
  if (typeof location != "undefined" && typeof location.reload == "function")
4571
4682
  location.reload();
@@ -0,0 +1,5 @@
1
+ export declare function rememberOfflineLogin(realm: string, login: string, uid: number, password: string): Promise<void>;
2
+ export declare function verifyOfflineLogin(realm: string, login: string, uid: number, password: string): Promise<boolean>;
3
+ export declare function hasOfflineLogin(realm: string, uid: number): boolean;
4
+ export declare function forgetOfflineLogin(): void;
5
+ //# sourceMappingURL=offline-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline-auth.d.ts","sourceRoot":"","sources":["../src/offline-auth.ts"],"names":[],"mappings":"AAYA,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrH;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAYtH;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAKnE;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -1,9 +1,103 @@
1
- import {Api} from 'https://datalynk-client.primary.auxilium.world/dist/index.mjs';
2
-
3
- let api = null;
4
1
  const CACHE_NAME = 'datalynk';
2
+ const STATE_CACHE = 'datalynk-app-state';
5
3
  let ONLINE = true;
6
4
  const netChan = 'BroadcastChannel' in self ? new BroadcastChannel('sw-net') : null;
5
+ let updateQueue = Promise.resolve();
6
+ let settingsQueue = Promise.resolve();
7
+
8
+ function safeAsset(url) {
9
+ return ['http:', 'https:'].includes(url.protocol)
10
+ && !/\/(api|login|logout|settings)(\/|$)/i.test(url.pathname)
11
+ && ![...url.searchParams.keys()].some(key => /token|auth|secret|key|session|password/i.test(key));
12
+ }
13
+
14
+ function assetKey(url, destination) {
15
+ const key = new URL(url);
16
+ if(destination !== 'navigate' && destination !== 'json') key.searchParams.delete('bust');
17
+ if(destination === 'navigate') key.search = '';
18
+ return key.href;
19
+ }
20
+
21
+ async function savedAsset(cache, request, destination) {
22
+ const key = assetKey(request.url || request, destination);
23
+ return await cache.match(key) || (destination !== 'json' && await cache.match(key, {ignoreSearch: true}));
24
+ }
25
+
26
+ async function removeOtherVersions(cache, key) {
27
+ const current = new URL(key);
28
+ for(const old of await cache.keys())
29
+ if(old.url !== key && new URL(old.url).origin === current.origin && new URL(old.url).pathname === current.pathname)
30
+ await cache.delete(old);
31
+ }
32
+
33
+ async function activeCacheName() {
34
+ const state = await caches.open(STATE_CACHE);
35
+ const response = await state.match(self.location.origin + '/datalynk-app-active');
36
+ return response ? response.text() : CACHE_NAME;
37
+ }
38
+
39
+ async function activeCache() {
40
+ return caches.open(await activeCacheName());
41
+ }
42
+
43
+ function updateAppAssets(values) {
44
+ updateQueue = updateQueue.catch(() => {}).then(async () => {
45
+ const urls = [...new Set(values.map(value => {
46
+ try {
47
+ const url = new URL(value, self.location.origin);
48
+ url.hash = '';
49
+ return safeAsset(url) && (url.origin === self.location.origin
50
+ || !/\.(?:png|jpe?g|gif|webp|svg)(?:$|\?)/i.test(url.href)) ? url.href : null;
51
+ } catch { return null; }
52
+ }).filter(Boolean))];
53
+ if(!urls.length) return;
54
+ const previousName = await activeCacheName();
55
+ const nextName = 'datalynk-app-' + Date.now() + '-' + Math.random().toString(36).slice(2);
56
+ const previous = await caches.open(previousName);
57
+ const next = await caches.open(nextName);
58
+ try {
59
+ for(const request of await previous.keys()) {
60
+ if(new URL(request.url).pathname === new URL(urls[0]).pathname
61
+ && new URL(request.url).origin === new URL(urls[0]).origin) continue;
62
+ const saved = await previous.match(request);
63
+ if(saved && safeAsset(new URL(request.url))) await next.put(request, saved);
64
+ }
65
+ for(const url of urls) {
66
+ const response = await fetch(url, {mode: new URL(url).origin === self.location.origin ? 'same-origin' : 'no-cors'});
67
+ if((response.status !== 200 && response.type !== 'opaque') || response.headers.has('set-cookie')
68
+ || /private|no-store/i.test(response.headers.get('cache-control') || '')
69
+ || (/\.json$/i.test(new URL(url).pathname) && !/\bpublic\b/i.test(response.headers.get('cache-control') || ''))
70
+ || (/\.(?:m?js|css)(?:$|\?)/i.test(url) && /text\/html/i.test(response.headers.get('content-type') || '')))
71
+ throw new Error('Offline app update incomplete: ' + url);
72
+ const destination = /\.json$/i.test(new URL(url).pathname) ? 'json' : 'asset';
73
+ if(response.type === 'opaque' && await savedAsset(previous, url, destination)) continue;
74
+ const key = assetKey(url, destination);
75
+ await next.put(key, response);
76
+ await removeOtherVersions(next, key);
77
+ }
78
+ for(const request of await previous.keys()) {
79
+ if(new URL(request.url).pathname === new URL(urls[0]).pathname
80
+ && new URL(request.url).origin === new URL(urls[0]).origin) continue;
81
+ if(urls.some(url => new URL(url).origin === new URL(request.url).origin
82
+ && new URL(url).pathname === new URL(request.url).pathname)) continue;
83
+ if(await next.match(request)) continue;
84
+ const saved = await previous.match(request);
85
+ if(saved && safeAsset(new URL(request.url))) await next.put(request, saved);
86
+ }
87
+ const state = await caches.open(STATE_CACHE);
88
+ await state.put(self.location.origin + '/datalynk-app-active', new Response(nextName));
89
+ } catch(error) {
90
+ await caches.delete(nextName);
91
+ console.warn(error);
92
+ return;
93
+ }
94
+ if(previousName !== nextName) {
95
+ try { await caches.delete(previousName); }
96
+ catch(error) { console.warn('Unable to remove previous app cache', error); }
97
+ }
98
+ });
99
+ return updateQueue;
100
+ }
7
101
 
8
102
  function broadcastStatus(online) {
9
103
  if(ONLINE === online) return;
@@ -32,6 +126,11 @@ async function setSettings(settings) {
32
126
  return newSettings;
33
127
  }
34
128
 
129
+ function saveSettings(settings) {
130
+ settingsQueue = settingsQueue.catch(() => {}).then(() => setSettings(settings));
131
+ return settingsQueue;
132
+ }
133
+
35
134
  self.addEventListener('install', (event) => {
36
135
  event.waitUntil(self.skipWaiting());
37
136
  });
@@ -40,17 +139,22 @@ self.addEventListener('activate', (event) => {
40
139
  event.waitUntil((async () => {
41
140
  await self.clients.claim();
42
141
 
142
+ const appCache = await activeCache();
143
+ for(const request of await appCache.keys())
144
+ if(!safeAsset(new URL(request.url))
145
+ || (/\.json$/i.test(new URL(request.url).pathname)
146
+ && !/\bpublic\b/i.test((await appCache.match(request))?.headers.get('cache-control') || '')))
147
+ await appCache.delete(request);
148
+
43
149
  const keys = await caches.keys();
150
+ const activeName = await activeCacheName();
151
+ await Promise.all(keys.filter(k => k.startsWith('datalynk-app-')
152
+ && k !== STATE_CACHE && k !== activeName).map(k => caches.delete(k)));
44
153
  await Promise.all(
45
154
  keys.filter((k) => k.startsWith('app-cache-') && k !== CACHE_NAME)
46
155
  .map((k) => caches.delete(k))
47
156
  );
48
157
 
49
- const settings = await getSettings();
50
- if(settings?.url) {
51
- api = new Api(settings.url, { ...(settings.options || {}), serviceWorker: false, replayOfflineQueue: false });
52
- if(settings.token) api.token = settings.token;
53
- }
54
158
  })());
55
159
  });
56
160
 
@@ -76,32 +180,43 @@ self.addEventListener('fetch', (event) => {
76
180
  }
77
181
 
78
182
  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
-
183
+ const cache = await activeCache();
184
+ const url = new URL(request.url);
185
+ const appFile = request.mode === 'navigate' || ['script', 'style', 'image', 'font', 'worker'].includes(request.destination);
186
+ const staticJson = url.origin === self.location.origin && /\.json$/i.test(url.pathname) && !request.destination;
187
+ const cacheable = (appFile || staticJson) && safeAsset(url) && !request.headers.has('authorization')
188
+ && !request.headers.has('range') && (url.origin === self.location.origin || request.destination !== 'image');
189
+ const destination = request.mode === 'navigate' ? 'navigate' : staticJson ? 'json' : 'asset';
85
190
  try {
86
191
  const networkResponse = await fetch(request);
87
192
  broadcastStatus(true);
88
- if(networkResponse.status === 404) { // purge stale
89
- await cache.delete(request);
90
- return networkResponse;
193
+ if((networkResponse.status === 404 || networkResponse.status >= 500) && cacheable) {
194
+ const cached = await savedAsset(await activeCache(), request, destination);
195
+ if(cached) { broadcastStatus(false); return cached; }
91
196
  }
92
- const isCacheable = networkResponse.ok || networkResponse.type === 'opaque';
93
- if(isCacheable) {
197
+ const isCacheable = networkResponse.status === 200 || networkResponse.type === 'opaque';
198
+ const saved = cacheable && await savedAsset(cache, request, destination);
199
+ if(isCacheable && cacheable && (networkResponse.status === 200 || !saved)
200
+ && (!staticJson || /\bpublic\b/i.test(networkResponse.headers.get('cache-control') || ''))
201
+ && !networkResponse.headers.has('set-cookie')
202
+ && !/private|no-store/i.test(networkResponse.headers.get('cache-control') || '')
203
+ && !(/\.(?:m?js|css)(?:$|\?)/i.test(url.href) && /text\/html/i.test(networkResponse.headers.get('content-type') || ''))) {
94
204
  const isOpaqueNav = networkResponse.type === 'opaque' && request.mode === 'navigate';
95
205
  if (!isOpaqueNav) {
96
- event.waitUntil(cache.put(request, networkResponse.clone()));
206
+ event.waitUntil((async () => {
207
+ const current = await activeCache();
208
+ const key = assetKey(url.href, destination);
209
+ await current.put(key, networkResponse.clone());
210
+ if(destination !== 'json') await removeOtherVersions(current, key);
211
+ })().catch(error => console.warn('Unable to save offline app file', error)));
97
212
  }
98
213
  }
99
214
  return networkResponse;
100
215
  } catch {
101
216
  broadcastStatus(false);
102
- const cached = await cache.match(request);
217
+ const cached = cacheable && await savedAsset(await activeCache(), request, destination);
103
218
  if(cached) return cached;
104
- if(request.mode === 'navigate') {
219
+ if(cacheable && request.mode === 'navigate') {
105
220
  const fallback = (await cache.match('/')) || (await cache.match('/index.html'));
106
221
  if(fallback) return fallback;
107
222
  }
@@ -116,18 +231,20 @@ self.addEventListener('fetch', (event) => {
116
231
  });
117
232
 
118
233
  self.addEventListener('message', async (event) => {
234
+ if(Array.isArray(event.data?.offlineAssets)) {
235
+ event.waitUntil(updateAppAssets(event.data.offlineAssets));
236
+ }
119
237
  if(event.data?.options) { // ๐Ÿ”ง Update settings
120
- api = new Api(event.data.options.url, { ...event.data.options, serviceWorker: false, replayOfflineQueue: false });
121
- await setSettings(event.data.options);
238
+ event.waitUntil(saveSettings(event.data.options));
122
239
  }
123
240
  // `null` is an intentional token update during logout/expiry, so check for the
124
241
  // property rather than truthiness.
125
242
  if(event.data && Object.prototype.hasOwnProperty.call(event.data, 'token')) { // ๐Ÿ”‘ Update token
126
243
  const token = event.data.token ?? null;
127
- if(api) api.token = token;
128
- await setSettings({token});
244
+ event.waitUntil(saveSettings({token}));
129
245
  }
130
246
  if(event.data?.clearCache) { // ๐Ÿงน Clear cache
131
- await caches.delete(CACHE_NAME);
247
+ await caches.delete(await activeCacheName());
248
+ await caches.delete(STATE_CACHE);
132
249
  }
133
250
  });
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.6.0",
5
+ "version": "1.6.2",
6
6
  "author": "Auxilium Group <devops@auxiliumgroup.com>",
7
7
  "private": false,
8
8
  "main": "./dist/index.cjs",