@auxilium/datalynk-client 1.5.1 → 1.5.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.
File without changes
package/dist/api.d.ts CHANGED
@@ -41,12 +41,14 @@ export type ApiOptions = {
41
41
  * connectivity or when Datalynk enters an `unavailable` recovery state.
42
42
  */
43
43
  offline?: number[];
44
- /** Display an offline banner */
44
+ /** @deprecated Connectivity banners are no longer rendered by the SDK. Retained as a no-op for compatibility. */
45
45
  offlineBanner?: boolean | 'top' | 'bottom';
46
46
  /** Website hostname */
47
47
  origin?: string;
48
48
  /** Save session token to localStorage to persist logins */
49
49
  saveSession?: boolean;
50
+ /** Replay the IndexedDB pending queue when authenticated connectivity returns. Disable in service-worker helper instances. */
51
+ replayOfflineQueue?: boolean;
50
52
  /** Watch JWT expiry. This is always disabled on development hosts. */
51
53
  watchTokenExpiry?: boolean;
52
54
  /** Service worker URL */
@@ -89,8 +91,9 @@ export interface ApiRequestOptions {
89
91
  /**
90
92
  * Allow this request to be queued while normal API access is unavailable.
91
93
  *
92
- * Queued requests do not receive an immediate response payload. Unauthorized
93
- * or expired sessions are never written to the replay queue.
94
+ * Queued requests do not receive an immediate response payload. If a token
95
+ * expires while Datalynk is offline, queued work is preserved and replay is
96
+ * deferred until the same user successfully reauthenticates.
94
97
  */
95
98
  offline?: boolean;
96
99
  /** Skip the token translating step */
@@ -143,7 +146,14 @@ export declare class Api {
143
146
  /** Track online state */
144
147
  private heartbeat;
145
148
  private authenticationInvalid;
149
+ /** Token reached exp while the client could not reach Datalynk; keep the offline session alive until reconnect. */
150
+ private offlineAuthenticationExpired;
146
151
  private tokenExpiryTimeout;
152
+ private unauthorizedLogout;
153
+ private replayingPending;
154
+ /** If replay is requested while a replay pass is already active, run another pass when it finishes. */
155
+ private pendingReplayRequested;
156
+ private offlineSessionOwner;
147
157
  /** Request-specific recovery after a server response proves an API call is broken. */
148
158
  private recovery;
149
159
  /** Retry the failed request twice immediately, then this long after each failed recovery response. */
@@ -151,6 +161,8 @@ export declare class Api {
151
161
  private readonly recoveryImmediateRetries;
152
162
  /** LocalStorage key for persisting logins */
153
163
  private localStorageKey;
164
+ /** Persisted owner of an offline queue so another login cannot inherit queued writes. */
165
+ private offlineSessionOwnerKey;
154
166
  private tokenStorageListener;
155
167
  /** Pending requests cache */
156
168
  private pending;
@@ -183,6 +195,8 @@ export declare class Api {
183
195
  version: string;
184
196
  /** Is token expired */
185
197
  get expired(): boolean;
198
+ /** Whether authentication expired while offline and must be renewed before replaying queued work. */
199
+ get reauthenticationPending(): boolean;
186
200
  /** Get session info from JWT payload */
187
201
  get jwtPayload(): JwtPayload | null;
188
202
  private onlineOverride;
@@ -247,9 +261,14 @@ export declare class Api {
247
261
  private _request;
248
262
  /** Execute exactly one HTTP API attempt. Recovery uses this directly to avoid recursive retry loops. */
249
263
  private _requestOnce;
250
- /** Only server-response failures own global recovery; auth and ordinary 4xx errors do not. */
264
+ /**
265
+ * Only failures which indicate Datalynk as a service is unavailable own global
266
+ * recovery. A request-specific application failure must not take the whole
267
+ * client offline: another unrelated API request may still be perfectly healthy.
268
+ */
251
269
  private isRecoverableResponseError;
252
- private isMysqlError;
270
+ /** Any API error returned in PDO SQLSTATE form uses the offline/recovery path. */
271
+ private isSqlStateError;
253
272
  /**
254
273
  * A returned server failure immediately makes the client unavailable. The exact
255
274
  * failed request is then retried twice back-to-back. After that, retries are
@@ -262,15 +281,30 @@ export declare class Api {
262
281
  private finishRecovery;
263
282
  private cancelRecovery;
264
283
  private checkConnection;
284
+ private isGatewayUnavailableStatus;
265
285
  private isTokenExpired;
266
286
  private isDevelopmentEnvironment;
267
287
  private canAdoptToken;
268
288
  /** Adopt a newer canonical token written by another auth path before expiring this session. */
269
289
  private adoptStoredToken;
270
290
  private scheduleTokenExpiry;
291
+ /** Handle local JWT expiry without treating an offline device like a rejected online session. */
292
+ private expireToken;
293
+ private shouldDeferLocalExpiry;
294
+ private deferOfflineExpiry;
271
295
  private markUnauthorized;
296
+ private finishUnauthorizedLogout;
297
+ private clearOfflineSessionCache;
298
+ private tokenIdentity;
299
+ private readOfflineSessionOwner;
300
+ private ownersMatch;
301
+ private rememberOfflineSessionOwner;
302
+ private ensureOfflineSessionOwner;
303
+ private hasOwnedOfflineSession;
304
+ private clearOfflineSessionOwner;
305
+ /** Replay queued writes only after a matching authenticated session is online. */
306
+ private replayPendingQueue;
272
307
  private setConnectionStatus;
273
- private offlineBanner;
274
308
  private startHeartbeat;
275
309
  private stopHeartbeat;
276
310
  /**
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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"}
@@ -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
@@ -1553,7 +1553,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1553
1553
  }
1554
1554
  });
1555
1555
  if ((_a = this.api.options.offline) == null ? void 0 : _a.length)
1556
- this.user$.pipe(filter((u) => u !== void 0)).subscribe((u) => localStorage.setItem("datalynk-user", JSON.stringify(u)));
1556
+ 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));
1559
+ });
1557
1560
  }
1558
1561
  /** Current user */
1559
1562
  get user() {
@@ -1589,7 +1592,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1589
1592
  * @return {Promise<void>} Login complete
1590
1593
  */
1591
1594
  async handleLogin(spoke, options) {
1592
- var _a, _b;
1595
+ var _a, _b, _c;
1593
1596
  if (this.onlinePrompt) window.removeEventListener("online", this.onlinePrompt);
1594
1597
  if ((_a = this.api.options.offline) == null ? void 0 : _a.length) window.removeEventListener("online", this.onlinePrompt = () => this.handleLogin(spoke, options));
1595
1598
  const urlParams = new URLSearchParams(location.search);
@@ -1598,8 +1601,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1598
1601
  this.api.token = urlToken;
1599
1602
  urlParams.delete("datalynkToken");
1600
1603
  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) {
1605
+ return;
1601
1606
  } else if (this.api.token && !this.api.expired) {
1602
- if (((_b = this.api.jwtPayload) == null ? void 0 : _b.realm) != spoke) {
1607
+ if (((_c = this.api.jwtPayload) == null ? void 0 : _c.realm) != spoke) {
1603
1608
  this.api.token = null;
1604
1609
  location.reload();
1605
1610
  }
@@ -3229,7 +3234,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3229
3234
  } });
3230
3235
  }
3231
3236
  }
3232
- const version = "1.5.1";
3237
+ const version = "1.5.2";
3233
3238
  class WebRtc {
3234
3239
  constructor(api) {
3235
3240
  __publicField(this, "ice");
@@ -3776,7 +3781,14 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3776
3781
  timeout: 6e4
3777
3782
  });
3778
3783
  __publicField(this, "authenticationInvalid", false);
3784
+ /** Token reached exp while the client could not reach Datalynk; keep the offline session alive until reconnect. */
3785
+ __publicField(this, "offlineAuthenticationExpired", false);
3779
3786
  __publicField(this, "tokenExpiryTimeout", null);
3787
+ __publicField(this, "unauthorizedLogout", null);
3788
+ __publicField(this, "replayingPending", false);
3789
+ /** If replay is requested while a replay pass is already active, run another pass when it finishes. */
3790
+ __publicField(this, "pendingReplayRequested", false);
3791
+ __publicField(this, "offlineSessionOwner", null);
3780
3792
  /** Request-specific recovery after a server response proves an API call is broken. */
3781
3793
  __publicField(this, "recovery", null);
3782
3794
  /** Retry the failed request twice immediately, then this long after each failed recovery response. */
@@ -3784,6 +3796,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3784
3796
  __publicField(this, "recoveryImmediateRetries", 2);
3785
3797
  /** LocalStorage key for persisting logins */
3786
3798
  __publicField(this, "localStorageKey", "datalynk-token");
3799
+ /** Persisted owner of an offline queue so another login cannot inherit queued writes. */
3800
+ __publicField(this, "offlineSessionOwnerKey", "datalynk-offline-owner");
3787
3801
  __publicField(this, "tokenStorageListener", null);
3788
3802
  /** Pending requests cache */
3789
3803
  __publicField(this, "pending", {});
@@ -3841,6 +3855,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3841
3855
  offline: [],
3842
3856
  origin: typeof location !== "undefined" ? location.host : "Unknown",
3843
3857
  saveSession: true,
3858
+ replayOfflineQueue: true,
3844
3859
  watchTokenExpiry: true,
3845
3860
  serviceWorker: "/service.worker.mjs",
3846
3861
  ...options,
@@ -3883,21 +3898,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3883
3898
  this.webrtc = new WebRtc(this);
3884
3899
  if (typeof indexedDB != "undefined") {
3885
3900
  this.database = new Database("datalynk", ["pending", ...this.options.offline || []]);
3886
- this.online$.subscribe(async (online) => {
3887
- var _a2;
3888
- if (!online) return;
3889
- const table = (_a2 = this.database) == null ? void 0 : _a2.table("pending");
3890
- const keys = await (table == null ? void 0 : table.getAllKeys());
3891
- await Promise.allSettled(keys.map(async (k) => {
3892
- const r = await (table == null ? void 0 : table.get(k));
3893
- await this.request(r).then(() => table == null ? void 0 : table.delete(k));
3894
- }));
3901
+ if (this.options.replayOfflineQueue !== false) this.online$.subscribe((online) => {
3902
+ if (online) void this.replayPendingQueue();
3895
3903
  });
3896
3904
  }
3897
3905
  if (typeof window !== "undefined") {
3898
3906
  window.addEventListener("online", () => this.checkConnection());
3899
3907
  window.addEventListener("offline", () => this.setConnectionStatus("offline"));
3900
- this.online$.subscribe(() => this.offlineBanner());
3901
3908
  this.startHeartbeat();
3902
3909
  }
3903
3910
  if ((_a = this.options.offline) == null ? void 0 : _a.length) {
@@ -3928,6 +3935,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3928
3935
  var _a;
3929
3936
  return (((_a = this.jwtPayload) == null ? void 0 : _a.exp) ?? Infinity) * 1e3 <= Date.now();
3930
3937
  }
3938
+ /** Whether authentication expired while offline and must be renewed before replaying queued work. */
3939
+ get reauthenticationPending() {
3940
+ return this.offlineAuthenticationExpired;
3941
+ }
3931
3942
  /** Get session info from JWT payload */
3932
3943
  get jwtPayload() {
3933
3944
  if (!this.token) return null;
@@ -3986,21 +3997,26 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3986
3997
  return this.token$.getValue();
3987
3998
  }
3988
3999
  set token(token) {
4000
+ var _a;
3989
4001
  if (this.recovery && token !== this.recovery.token)
3990
4002
  this.cancelRecovery(errorFromCode(401, "Session changed during API recovery"));
3991
4003
  if (token && this.isTokenExpired(token)) {
3992
4004
  if (this.adoptStoredToken(token)) return;
3993
- this.authenticationInvalid = true;
3994
- this.token$.next(null);
3995
- this.setConnectionStatus("unauthorized");
4005
+ if (this.shouldDeferLocalExpiry()) {
4006
+ this.deferOfflineExpiry(token);
4007
+ return;
4008
+ }
4009
+ this.markUnauthorized(void 0, this.hasOwnedOfflineSession(token));
3996
4010
  return;
3997
4011
  }
3998
4012
  this.authenticationInvalid = false;
4013
+ this.offlineAuthenticationExpired = false;
3999
4014
  this.token$.next(token);
4000
4015
  if (token && !this.online && !this.recovery) {
4001
4016
  const browserOnline = typeof navigator == "undefined" || typeof navigator.onLine == "undefined" || navigator.onLine;
4002
4017
  this.setConnectionStatus(browserOnline ? "online" : "offline");
4003
4018
  }
4019
+ if (token && ((_a = this.options) == null ? void 0 : _a.replayOfflineQueue) !== false) void this.replayPendingQueue();
4004
4020
  }
4005
4021
  async _request(req, options = {}) {
4006
4022
  if (this.recovery) throw errorFromCode(503, "Datalynk is unavailable");
@@ -4019,7 +4035,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4019
4035
  async _requestOnce(req, options = {}, recoveryAttempt = false) {
4020
4036
  const token = options.token || this.token;
4021
4037
  if (token && this.isTokenExpired(token)) {
4022
- this.markUnauthorized(token);
4038
+ if (token === this.token) this.expireToken(token);
4023
4039
  throw errorFromCode(401, "Session token expired");
4024
4040
  }
4025
4041
  let resp;
@@ -4055,7 +4071,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4055
4071
  if (!resp.ok || (data == null ? void 0 : data.error)) {
4056
4072
  const error = Object.assign(errorFromCode(resp.status, data == null ? void 0 : data.error), data);
4057
4073
  Object.defineProperty(error, "response", { value: resp, configurable: true });
4058
- if (!recoveryAttempt && resp.status < 500 && resp.status !== 401 && !this.isMysqlError(error))
4074
+ if (!recoveryAttempt && resp.status !== 401 && !this.isSqlStateError(error))
4059
4075
  this.setConnectionStatus("online");
4060
4076
  throw error;
4061
4077
  }
@@ -4063,17 +4079,22 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4063
4079
  if (!recoveryAttempt) this.setConnectionStatus("online");
4064
4080
  return data;
4065
4081
  }
4066
- /** Only server-response failures own global recovery; auth and ordinary 4xx errors do not. */
4082
+ /**
4083
+ * Only failures which indicate Datalynk as a service is unavailable own global
4084
+ * recovery. A request-specific application failure must not take the whole
4085
+ * client offline: another unrelated API request may still be perfectly healthy.
4086
+ */
4067
4087
  isRecoverableResponseError(error) {
4068
4088
  var _a;
4069
- if (error instanceof UnexpectedApiResponseError) return true;
4070
4089
  if ((error == null ? void 0 : error.code) === 401) return false;
4071
4090
  const status = ((_a = error == null ? void 0 : error.response) == null ? void 0 : _a.status) ?? (error == null ? void 0 : error.code);
4072
- return status >= 500 || this.isMysqlError(error);
4091
+ if (this.isGatewayUnavailableStatus(status)) return true;
4092
+ return this.isSqlStateError(error);
4073
4093
  }
4074
- isMysqlError(error) {
4075
- const message = String((error == null ? void 0 : error.error) ?? (error == null ? void 0 : error.message) ?? "");
4076
- return /\bSQLSTATE\[[A-Z0-9]+\]/i.test(message);
4094
+ /** Any API error returned in PDO SQLSTATE form uses the offline/recovery path. */
4095
+ isSqlStateError(error) {
4096
+ const message = String((error == null ? void 0 : error.error) ?? (error == null ? void 0 : error.message) ?? "").trim();
4097
+ return /^SQLSTATE\[/i.test(message);
4077
4098
  }
4078
4099
  /**
4079
4100
  * A returned server failure immediately makes the client unavailable. The exact
@@ -4166,8 +4187,22 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4166
4187
  this.setConnectionStatus("unauthorized");
4167
4188
  return;
4168
4189
  }
4190
+ if (this.offlineAuthenticationExpired) {
4191
+ const controller2 = new AbortController();
4192
+ const timeout2 = setTimeout(() => controller2.abort(), this.heartbeat.timeout);
4193
+ try {
4194
+ const response = await fetch(this.url + this.heartbeat.target, { signal: controller2.signal });
4195
+ if (this.isGatewayUnavailableStatus(response.status)) this.setConnectionStatus("unavailable");
4196
+ else this.markUnauthorized(this.token, true);
4197
+ } catch {
4198
+ this.setConnectionStatus("offline");
4199
+ } finally {
4200
+ clearTimeout(timeout2);
4201
+ }
4202
+ return;
4203
+ }
4169
4204
  if (this.token && this.isTokenExpired(this.token)) {
4170
- this.markUnauthorized(this.token);
4205
+ this.expireToken(this.token);
4171
4206
  return;
4172
4207
  }
4173
4208
  const controller = new AbortController();
@@ -4175,13 +4210,16 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4175
4210
  try {
4176
4211
  const response = await fetch(this.url + this.heartbeat.target, { signal: controller.signal });
4177
4212
  if (this.recovery) return;
4178
- this.setConnectionStatus(response.ok ? "online" : "unavailable");
4213
+ this.setConnectionStatus(this.isGatewayUnavailableStatus(response.status) ? "unavailable" : "online");
4179
4214
  } catch (error) {
4180
4215
  if (!this.recovery) this.setConnectionStatus("offline");
4181
4216
  } finally {
4182
4217
  clearTimeout(timeout);
4183
4218
  }
4184
4219
  }
4220
+ isGatewayUnavailableStatus(status) {
4221
+ return status === 502 || status === 503 || status === 504;
4222
+ }
4185
4223
  isTokenExpired(token) {
4186
4224
  var _a;
4187
4225
  try {
@@ -4231,45 +4269,241 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4231
4269
  try {
4232
4270
  expiresAt = (((_a = decodeJwt(token)) == null ? void 0 : _a.exp) ?? 0) * 1e3;
4233
4271
  } catch {
4234
- if (!this.adoptStoredToken(token)) this.markUnauthorized(token);
4272
+ if (!this.adoptStoredToken(token)) this.expireToken(token);
4235
4273
  return;
4236
4274
  }
4237
4275
  const delay = expiresAt - Date.now();
4238
4276
  if (delay <= 0) {
4239
- if (!this.adoptStoredToken(token)) this.markUnauthorized(token);
4277
+ if (!this.adoptStoredToken(token)) this.expireToken(token);
4240
4278
  return;
4241
4279
  }
4242
4280
  this.tokenExpiryTimeout = setTimeout(() => {
4243
4281
  if (delay > 2147e6) this.scheduleTokenExpiry(token);
4244
- else if (!this.adoptStoredToken(token)) this.markUnauthorized(token);
4282
+ else if (!this.adoptStoredToken(token)) this.expireToken(token);
4245
4283
  }, Math.min(delay, 2147e6));
4246
4284
  (_c = (_b = this.tokenExpiryTimeout) == null ? void 0 : _b.unref) == null ? void 0 : _c.call(_b);
4247
4285
  }
4248
- markUnauthorized(token) {
4286
+ /** Handle local JWT expiry without treating an offline device like a rejected online session. */
4287
+ expireToken(token) {
4288
+ if (token !== this.token) return;
4289
+ if (this.shouldDeferLocalExpiry()) {
4290
+ this.deferOfflineExpiry(token);
4291
+ return;
4292
+ }
4293
+ this.markUnauthorized(token, this.hasOwnedOfflineSession(token));
4294
+ }
4295
+ shouldDeferLocalExpiry() {
4296
+ if (this.authenticationInvalid) return false;
4297
+ if (typeof navigator != "undefined" && typeof navigator.onLine != "undefined" && !navigator.onLine) return true;
4298
+ return this.status === "offline" || this.status === "unavailable";
4299
+ }
4300
+ deferOfflineExpiry(token) {
4301
+ this.authenticationInvalid = false;
4302
+ this.offlineAuthenticationExpired = true;
4303
+ this.cancelRecovery(errorFromCode(401, "Session expired while offline"));
4304
+ this.rememberOfflineSessionOwner(token);
4305
+ if (this.token !== token) this.token$.next(token);
4306
+ if (this.status === "online") this.setConnectionStatus("offline");
4307
+ }
4308
+ markUnauthorized(token, preservePending = this.hasOwnedOfflineSession(token || this.token)) {
4249
4309
  if (token && token !== this.token) return;
4250
4310
  if (this.adoptStoredToken(token)) return;
4311
+ if (this.authenticationInvalid && this.unauthorizedLogout) return;
4251
4312
  this.authenticationInvalid = true;
4313
+ this.offlineAuthenticationExpired = false;
4252
4314
  this.cancelRecovery(errorFromCode(401, "Session expired"));
4253
- if (this.token != null) this.token$.next(null);
4315
+ if (this.token !== null) this.token$.next(null);
4254
4316
  this.setConnectionStatus("unauthorized");
4317
+ this.unauthorizedLogout ?? (this.unauthorizedLogout = Promise.resolve().then(() => this.finishUnauthorizedLogout(preservePending)).finally(() => {
4318
+ this.unauthorizedLogout = null;
4319
+ }));
4320
+ }
4321
+ async finishUnauthorizedLogout(preservePending = false) {
4322
+ var _a;
4323
+ if (typeof localStorage != "undefined") {
4324
+ localStorage.removeItem(this.localStorageKey);
4325
+ localStorage.removeItem("datalynk-user");
4326
+ if (!preservePending) localStorage.removeItem(this.offlineSessionOwnerKey);
4327
+ }
4328
+ if (!preservePending) this.offlineSessionOwner = null;
4329
+ for (const slice of this.sliceCache.values()) {
4330
+ try {
4331
+ slice.sync(false);
4332
+ } catch {
4333
+ }
4334
+ }
4335
+ await this.clearOfflineSessionCache(preservePending);
4336
+ if (typeof caches != "undefined") {
4337
+ try {
4338
+ await Promise.all([caches.delete("datalynk"), caches.delete("api-settings")]);
4339
+ } catch (error) {
4340
+ console.error("Unable to clear Datalynk service worker cache", error);
4341
+ }
4342
+ }
4343
+ if (typeof navigator != "undefined" && ((_a = navigator.serviceWorker) == null ? void 0 : _a.controller)) {
4344
+ navigator.serviceWorker.controller.postMessage({ token: null, clearCache: true });
4345
+ }
4346
+ if (typeof location != "undefined" && typeof location.reload == "function")
4347
+ location.reload();
4348
+ }
4349
+ async clearOfflineSessionCache(preservePending = false) {
4350
+ if (typeof indexedDB == "undefined") return;
4351
+ try {
4352
+ if (this.database) {
4353
+ const db = await this.database.connection;
4354
+ const stores = Array.from(db.objectStoreNames).filter((name) => !(preservePending && name === "pending"));
4355
+ if (!stores.length) return;
4356
+ await new Promise((resolve, reject) => {
4357
+ const tx = db.transaction(stores, "readwrite");
4358
+ stores.forEach((name) => tx.objectStore(name).clear());
4359
+ tx.oncomplete = () => resolve();
4360
+ tx.onerror = () => reject(tx.error);
4361
+ tx.onabort = () => reject(tx.error);
4362
+ });
4363
+ return;
4364
+ }
4365
+ await new Promise((resolve, reject) => {
4366
+ const request = indexedDB.open("datalynk");
4367
+ request.onerror = () => reject(request.error);
4368
+ request.onsuccess = () => {
4369
+ const db = request.result;
4370
+ const stores = Array.from(db.objectStoreNames).filter((name) => !(preservePending && name === "pending"));
4371
+ if (!stores.length) {
4372
+ db.close();
4373
+ resolve();
4374
+ return;
4375
+ }
4376
+ const tx = db.transaction(stores, "readwrite");
4377
+ stores.forEach((name) => tx.objectStore(name).clear());
4378
+ tx.oncomplete = () => {
4379
+ db.close();
4380
+ resolve();
4381
+ };
4382
+ tx.onerror = () => {
4383
+ const error = tx.error;
4384
+ db.close();
4385
+ reject(error);
4386
+ };
4387
+ tx.onabort = () => {
4388
+ const error = tx.error;
4389
+ db.close();
4390
+ reject(error);
4391
+ };
4392
+ };
4393
+ });
4394
+ } catch (error) {
4395
+ console.error("Unable to clear Datalynk offline session cache", error);
4396
+ }
4397
+ }
4398
+ tokenIdentity(token) {
4399
+ if (!token) return null;
4400
+ try {
4401
+ const payload = decodeJwt(token);
4402
+ const user = (payload == null ? void 0 : payload.uid) ?? (payload == null ? void 0 : payload.sub);
4403
+ return {
4404
+ realm: (payload == null ? void 0 : payload.realm) == null ? void 0 : String(payload.realm),
4405
+ user: user == null ? void 0 : String(user)
4406
+ };
4407
+ } catch {
4408
+ return null;
4409
+ }
4410
+ }
4411
+ readOfflineSessionOwner() {
4412
+ if (this.offlineSessionOwner) return this.offlineSessionOwner;
4413
+ if (typeof localStorage == "undefined") return null;
4414
+ try {
4415
+ const value = localStorage.getItem(this.offlineSessionOwnerKey);
4416
+ if (!value) return null;
4417
+ const owner = JSON.parse(value);
4418
+ if (!owner || typeof owner !== "object") return null;
4419
+ return this.offlineSessionOwner = owner;
4420
+ } catch {
4421
+ return null;
4422
+ }
4423
+ }
4424
+ ownersMatch(owner, identity2) {
4425
+ if (!identity2) return false;
4426
+ if (owner.realm != null && owner.realm !== identity2.realm) return false;
4427
+ if (owner.user != null && owner.user !== identity2.user) return false;
4428
+ return true;
4429
+ }
4430
+ rememberOfflineSessionOwner(token) {
4431
+ const identity2 = this.tokenIdentity(token);
4432
+ if (!identity2) return;
4433
+ const existing = this.readOfflineSessionOwner();
4434
+ if (existing && !this.ownersMatch(existing, identity2)) return;
4435
+ this.offlineSessionOwner = identity2;
4436
+ if (typeof localStorage != "undefined") {
4437
+ try {
4438
+ localStorage.setItem(this.offlineSessionOwnerKey, JSON.stringify(identity2));
4439
+ } catch {
4440
+ }
4441
+ }
4442
+ }
4443
+ ensureOfflineSessionOwner(token) {
4444
+ const existing = this.readOfflineSessionOwner();
4445
+ const identity2 = this.tokenIdentity(token);
4446
+ if (existing) return this.ownersMatch(existing, identity2);
4447
+ if (!identity2) return true;
4448
+ this.rememberOfflineSessionOwner(token);
4449
+ return true;
4450
+ }
4451
+ hasOwnedOfflineSession(token) {
4452
+ const owner = this.readOfflineSessionOwner();
4453
+ return !!owner && this.ownersMatch(owner, this.tokenIdentity(token));
4454
+ }
4455
+ clearOfflineSessionOwner() {
4456
+ this.offlineSessionOwner = null;
4457
+ if (typeof localStorage != "undefined") {
4458
+ try {
4459
+ localStorage.removeItem(this.offlineSessionOwnerKey);
4460
+ } catch {
4461
+ }
4462
+ }
4463
+ }
4464
+ /** Replay queued writes only after a matching authenticated session is online. */
4465
+ async replayPendingQueue() {
4466
+ if (!this.database || !this.online || this.authenticationInvalid || this.offlineAuthenticationExpired) return;
4467
+ if (this.replayingPending) {
4468
+ this.pendingReplayRequested = true;
4469
+ return;
4470
+ }
4471
+ const table = this.database.table("pending");
4472
+ const owner = this.readOfflineSessionOwner();
4473
+ if (owner && !this.ownersMatch(owner, this.tokenIdentity(this.token))) {
4474
+ if (this.token) console.warn("Datalynk offline queue is quarantined because it belongs to another authenticated user");
4475
+ return;
4476
+ }
4477
+ this.replayingPending = true;
4478
+ try {
4479
+ const keys = await table.getAllKeys();
4480
+ for (const key of keys) {
4481
+ if (!this.online || this.authenticationInvalid || this.offlineAuthenticationExpired) break;
4482
+ if (owner && !this.ownersMatch(owner, this.tokenIdentity(this.token))) break;
4483
+ const request = await table.get(key);
4484
+ if (request == null) continue;
4485
+ try {
4486
+ await this.request(request);
4487
+ await table.delete(key);
4488
+ } catch (error) {
4489
+ break;
4490
+ }
4491
+ }
4492
+ if (await table.count() === 0) this.clearOfflineSessionOwner();
4493
+ } finally {
4494
+ this.replayingPending = false;
4495
+ if (this.pendingReplayRequested) {
4496
+ this.pendingReplayRequested = false;
4497
+ if (this.online && !this.authenticationInvalid && !this.offlineAuthenticationExpired)
4498
+ void this.replayPendingQueue();
4499
+ }
4500
+ }
4255
4501
  }
4256
4502
  setConnectionStatus(status) {
4257
4503
  if (this.status !== status) this.status$.next(status);
4258
4504
  const online = status === "online";
4259
4505
  if (this.online !== online) this.online$.next(online);
4260
4506
  }
4261
- offlineBanner() {
4262
- if (this.options.offlineBanner === false || typeof document == "undefined") return;
4263
- if (this.status === "online") {
4264
- removeBanner("datalynk-offline-banner");
4265
- } else {
4266
- const message = this.status === "unauthorized" ? "⚠️ Your session expired, please sign in again" : this.status === "unavailable" ? "⚠️ Datalynk is currently unavailable" : "⚠️ You are offline, please reconnect to sync changes";
4267
- createBanner(message, {
4268
- id: "datalynk-offline-banner",
4269
- position: this.options.offlineBanner === "top" ? "top" : "bottom"
4270
- });
4271
- }
4272
- }
4273
4507
  startHeartbeat() {
4274
4508
  this.stopHeartbeat();
4275
4509
  this.heartbeat.interval = setInterval(() => this.checkConnection(), this.heartbeat.timeout / 2);
@@ -4379,6 +4613,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4379
4613
  if (this.offline && typeof navigator != "undefined") {
4380
4614
  if (this.status === "unauthorized") return Promise.reject(errorFromCode(401, "Session expired"));
4381
4615
  if (options.offline) {
4616
+ if (!this.ensureOfflineSessionOwner(this.token))
4617
+ return Promise.reject(errorFromCode(409, "Offline queue belongs to another authenticated user"));
4382
4618
  (_b = (_a = this.database) == null ? void 0 : _a.table("pending")) == null ? void 0 : _b.add(data, key);
4383
4619
  return Promise.resolve();
4384
4620
  }
package/dist/index.mjs CHANGED
@@ -1549,7 +1549,10 @@ class Auth {
1549
1549
  }
1550
1550
  });
1551
1551
  if ((_a = this.api.options.offline) == null ? void 0 : _a.length)
1552
- this.user$.pipe(filter((u) => u !== void 0)).subscribe((u) => localStorage.setItem("datalynk-user", JSON.stringify(u)));
1552
+ 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));
1555
+ });
1553
1556
  }
1554
1557
  /** Current user */
1555
1558
  get user() {
@@ -1585,7 +1588,7 @@ class Auth {
1585
1588
  * @return {Promise<void>} Login complete
1586
1589
  */
1587
1590
  async handleLogin(spoke, options) {
1588
- var _a, _b;
1591
+ var _a, _b, _c;
1589
1592
  if (this.onlinePrompt) window.removeEventListener("online", this.onlinePrompt);
1590
1593
  if ((_a = this.api.options.offline) == null ? void 0 : _a.length) window.removeEventListener("online", this.onlinePrompt = () => this.handleLogin(spoke, options));
1591
1594
  const urlParams = new URLSearchParams(location.search);
@@ -1594,8 +1597,10 @@ class Auth {
1594
1597
  this.api.token = urlToken;
1595
1598
  urlParams.delete("datalynkToken");
1596
1599
  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) {
1601
+ return;
1597
1602
  } else if (this.api.token && !this.api.expired) {
1598
- if (((_b = this.api.jwtPayload) == null ? void 0 : _b.realm) != spoke) {
1603
+ if (((_c = this.api.jwtPayload) == null ? void 0 : _c.realm) != spoke) {
1599
1604
  this.api.token = null;
1600
1605
  location.reload();
1601
1606
  }
@@ -3225,7 +3230,7 @@ class Superuser {
3225
3230
  } });
3226
3231
  }
3227
3232
  }
3228
- const version = "1.5.1";
3233
+ const version = "1.5.2";
3229
3234
  class WebRtc {
3230
3235
  constructor(api) {
3231
3236
  __publicField(this, "ice");
@@ -3772,7 +3777,14 @@ const _Api = class _Api {
3772
3777
  timeout: 6e4
3773
3778
  });
3774
3779
  __publicField(this, "authenticationInvalid", false);
3780
+ /** Token reached exp while the client could not reach Datalynk; keep the offline session alive until reconnect. */
3781
+ __publicField(this, "offlineAuthenticationExpired", false);
3775
3782
  __publicField(this, "tokenExpiryTimeout", null);
3783
+ __publicField(this, "unauthorizedLogout", null);
3784
+ __publicField(this, "replayingPending", false);
3785
+ /** If replay is requested while a replay pass is already active, run another pass when it finishes. */
3786
+ __publicField(this, "pendingReplayRequested", false);
3787
+ __publicField(this, "offlineSessionOwner", null);
3776
3788
  /** Request-specific recovery after a server response proves an API call is broken. */
3777
3789
  __publicField(this, "recovery", null);
3778
3790
  /** Retry the failed request twice immediately, then this long after each failed recovery response. */
@@ -3780,6 +3792,8 @@ const _Api = class _Api {
3780
3792
  __publicField(this, "recoveryImmediateRetries", 2);
3781
3793
  /** LocalStorage key for persisting logins */
3782
3794
  __publicField(this, "localStorageKey", "datalynk-token");
3795
+ /** Persisted owner of an offline queue so another login cannot inherit queued writes. */
3796
+ __publicField(this, "offlineSessionOwnerKey", "datalynk-offline-owner");
3783
3797
  __publicField(this, "tokenStorageListener", null);
3784
3798
  /** Pending requests cache */
3785
3799
  __publicField(this, "pending", {});
@@ -3837,6 +3851,7 @@ const _Api = class _Api {
3837
3851
  offline: [],
3838
3852
  origin: typeof location !== "undefined" ? location.host : "Unknown",
3839
3853
  saveSession: true,
3854
+ replayOfflineQueue: true,
3840
3855
  watchTokenExpiry: true,
3841
3856
  serviceWorker: "/service.worker.mjs",
3842
3857
  ...options,
@@ -3879,21 +3894,13 @@ const _Api = class _Api {
3879
3894
  this.webrtc = new WebRtc(this);
3880
3895
  if (typeof indexedDB != "undefined") {
3881
3896
  this.database = new Database("datalynk", ["pending", ...this.options.offline || []]);
3882
- this.online$.subscribe(async (online) => {
3883
- var _a2;
3884
- if (!online) return;
3885
- const table = (_a2 = this.database) == null ? void 0 : _a2.table("pending");
3886
- const keys = await (table == null ? void 0 : table.getAllKeys());
3887
- await Promise.allSettled(keys.map(async (k) => {
3888
- const r = await (table == null ? void 0 : table.get(k));
3889
- await this.request(r).then(() => table == null ? void 0 : table.delete(k));
3890
- }));
3897
+ if (this.options.replayOfflineQueue !== false) this.online$.subscribe((online) => {
3898
+ if (online) void this.replayPendingQueue();
3891
3899
  });
3892
3900
  }
3893
3901
  if (typeof window !== "undefined") {
3894
3902
  window.addEventListener("online", () => this.checkConnection());
3895
3903
  window.addEventListener("offline", () => this.setConnectionStatus("offline"));
3896
- this.online$.subscribe(() => this.offlineBanner());
3897
3904
  this.startHeartbeat();
3898
3905
  }
3899
3906
  if ((_a = this.options.offline) == null ? void 0 : _a.length) {
@@ -3924,6 +3931,10 @@ const _Api = class _Api {
3924
3931
  var _a;
3925
3932
  return (((_a = this.jwtPayload) == null ? void 0 : _a.exp) ?? Infinity) * 1e3 <= Date.now();
3926
3933
  }
3934
+ /** Whether authentication expired while offline and must be renewed before replaying queued work. */
3935
+ get reauthenticationPending() {
3936
+ return this.offlineAuthenticationExpired;
3937
+ }
3927
3938
  /** Get session info from JWT payload */
3928
3939
  get jwtPayload() {
3929
3940
  if (!this.token) return null;
@@ -3982,21 +3993,26 @@ const _Api = class _Api {
3982
3993
  return this.token$.getValue();
3983
3994
  }
3984
3995
  set token(token) {
3996
+ var _a;
3985
3997
  if (this.recovery && token !== this.recovery.token)
3986
3998
  this.cancelRecovery(errorFromCode(401, "Session changed during API recovery"));
3987
3999
  if (token && this.isTokenExpired(token)) {
3988
4000
  if (this.adoptStoredToken(token)) return;
3989
- this.authenticationInvalid = true;
3990
- this.token$.next(null);
3991
- this.setConnectionStatus("unauthorized");
4001
+ if (this.shouldDeferLocalExpiry()) {
4002
+ this.deferOfflineExpiry(token);
4003
+ return;
4004
+ }
4005
+ this.markUnauthorized(void 0, this.hasOwnedOfflineSession(token));
3992
4006
  return;
3993
4007
  }
3994
4008
  this.authenticationInvalid = false;
4009
+ this.offlineAuthenticationExpired = false;
3995
4010
  this.token$.next(token);
3996
4011
  if (token && !this.online && !this.recovery) {
3997
4012
  const browserOnline = typeof navigator == "undefined" || typeof navigator.onLine == "undefined" || navigator.onLine;
3998
4013
  this.setConnectionStatus(browserOnline ? "online" : "offline");
3999
4014
  }
4015
+ if (token && ((_a = this.options) == null ? void 0 : _a.replayOfflineQueue) !== false) void this.replayPendingQueue();
4000
4016
  }
4001
4017
  async _request(req, options = {}) {
4002
4018
  if (this.recovery) throw errorFromCode(503, "Datalynk is unavailable");
@@ -4015,7 +4031,7 @@ const _Api = class _Api {
4015
4031
  async _requestOnce(req, options = {}, recoveryAttempt = false) {
4016
4032
  const token = options.token || this.token;
4017
4033
  if (token && this.isTokenExpired(token)) {
4018
- this.markUnauthorized(token);
4034
+ if (token === this.token) this.expireToken(token);
4019
4035
  throw errorFromCode(401, "Session token expired");
4020
4036
  }
4021
4037
  let resp;
@@ -4051,7 +4067,7 @@ const _Api = class _Api {
4051
4067
  if (!resp.ok || (data == null ? void 0 : data.error)) {
4052
4068
  const error = Object.assign(errorFromCode(resp.status, data == null ? void 0 : data.error), data);
4053
4069
  Object.defineProperty(error, "response", { value: resp, configurable: true });
4054
- if (!recoveryAttempt && resp.status < 500 && resp.status !== 401 && !this.isMysqlError(error))
4070
+ if (!recoveryAttempt && resp.status !== 401 && !this.isSqlStateError(error))
4055
4071
  this.setConnectionStatus("online");
4056
4072
  throw error;
4057
4073
  }
@@ -4059,17 +4075,22 @@ const _Api = class _Api {
4059
4075
  if (!recoveryAttempt) this.setConnectionStatus("online");
4060
4076
  return data;
4061
4077
  }
4062
- /** Only server-response failures own global recovery; auth and ordinary 4xx errors do not. */
4078
+ /**
4079
+ * Only failures which indicate Datalynk as a service is unavailable own global
4080
+ * recovery. A request-specific application failure must not take the whole
4081
+ * client offline: another unrelated API request may still be perfectly healthy.
4082
+ */
4063
4083
  isRecoverableResponseError(error) {
4064
4084
  var _a;
4065
- if (error instanceof UnexpectedApiResponseError) return true;
4066
4085
  if ((error == null ? void 0 : error.code) === 401) return false;
4067
4086
  const status = ((_a = error == null ? void 0 : error.response) == null ? void 0 : _a.status) ?? (error == null ? void 0 : error.code);
4068
- return status >= 500 || this.isMysqlError(error);
4087
+ if (this.isGatewayUnavailableStatus(status)) return true;
4088
+ return this.isSqlStateError(error);
4069
4089
  }
4070
- isMysqlError(error) {
4071
- const message = String((error == null ? void 0 : error.error) ?? (error == null ? void 0 : error.message) ?? "");
4072
- return /\bSQLSTATE\[[A-Z0-9]+\]/i.test(message);
4090
+ /** Any API error returned in PDO SQLSTATE form uses the offline/recovery path. */
4091
+ isSqlStateError(error) {
4092
+ const message = String((error == null ? void 0 : error.error) ?? (error == null ? void 0 : error.message) ?? "").trim();
4093
+ return /^SQLSTATE\[/i.test(message);
4073
4094
  }
4074
4095
  /**
4075
4096
  * A returned server failure immediately makes the client unavailable. The exact
@@ -4162,8 +4183,22 @@ const _Api = class _Api {
4162
4183
  this.setConnectionStatus("unauthorized");
4163
4184
  return;
4164
4185
  }
4186
+ if (this.offlineAuthenticationExpired) {
4187
+ const controller2 = new AbortController();
4188
+ const timeout2 = setTimeout(() => controller2.abort(), this.heartbeat.timeout);
4189
+ try {
4190
+ const response = await fetch(this.url + this.heartbeat.target, { signal: controller2.signal });
4191
+ if (this.isGatewayUnavailableStatus(response.status)) this.setConnectionStatus("unavailable");
4192
+ else this.markUnauthorized(this.token, true);
4193
+ } catch {
4194
+ this.setConnectionStatus("offline");
4195
+ } finally {
4196
+ clearTimeout(timeout2);
4197
+ }
4198
+ return;
4199
+ }
4165
4200
  if (this.token && this.isTokenExpired(this.token)) {
4166
- this.markUnauthorized(this.token);
4201
+ this.expireToken(this.token);
4167
4202
  return;
4168
4203
  }
4169
4204
  const controller = new AbortController();
@@ -4171,13 +4206,16 @@ const _Api = class _Api {
4171
4206
  try {
4172
4207
  const response = await fetch(this.url + this.heartbeat.target, { signal: controller.signal });
4173
4208
  if (this.recovery) return;
4174
- this.setConnectionStatus(response.ok ? "online" : "unavailable");
4209
+ this.setConnectionStatus(this.isGatewayUnavailableStatus(response.status) ? "unavailable" : "online");
4175
4210
  } catch (error) {
4176
4211
  if (!this.recovery) this.setConnectionStatus("offline");
4177
4212
  } finally {
4178
4213
  clearTimeout(timeout);
4179
4214
  }
4180
4215
  }
4216
+ isGatewayUnavailableStatus(status) {
4217
+ return status === 502 || status === 503 || status === 504;
4218
+ }
4181
4219
  isTokenExpired(token) {
4182
4220
  var _a;
4183
4221
  try {
@@ -4227,45 +4265,241 @@ const _Api = class _Api {
4227
4265
  try {
4228
4266
  expiresAt = (((_a = decodeJwt(token)) == null ? void 0 : _a.exp) ?? 0) * 1e3;
4229
4267
  } catch {
4230
- if (!this.adoptStoredToken(token)) this.markUnauthorized(token);
4268
+ if (!this.adoptStoredToken(token)) this.expireToken(token);
4231
4269
  return;
4232
4270
  }
4233
4271
  const delay = expiresAt - Date.now();
4234
4272
  if (delay <= 0) {
4235
- if (!this.adoptStoredToken(token)) this.markUnauthorized(token);
4273
+ if (!this.adoptStoredToken(token)) this.expireToken(token);
4236
4274
  return;
4237
4275
  }
4238
4276
  this.tokenExpiryTimeout = setTimeout(() => {
4239
4277
  if (delay > 2147e6) this.scheduleTokenExpiry(token);
4240
- else if (!this.adoptStoredToken(token)) this.markUnauthorized(token);
4278
+ else if (!this.adoptStoredToken(token)) this.expireToken(token);
4241
4279
  }, Math.min(delay, 2147e6));
4242
4280
  (_c = (_b = this.tokenExpiryTimeout) == null ? void 0 : _b.unref) == null ? void 0 : _c.call(_b);
4243
4281
  }
4244
- markUnauthorized(token) {
4282
+ /** Handle local JWT expiry without treating an offline device like a rejected online session. */
4283
+ expireToken(token) {
4284
+ if (token !== this.token) return;
4285
+ if (this.shouldDeferLocalExpiry()) {
4286
+ this.deferOfflineExpiry(token);
4287
+ return;
4288
+ }
4289
+ this.markUnauthorized(token, this.hasOwnedOfflineSession(token));
4290
+ }
4291
+ shouldDeferLocalExpiry() {
4292
+ if (this.authenticationInvalid) return false;
4293
+ if (typeof navigator != "undefined" && typeof navigator.onLine != "undefined" && !navigator.onLine) return true;
4294
+ return this.status === "offline" || this.status === "unavailable";
4295
+ }
4296
+ deferOfflineExpiry(token) {
4297
+ this.authenticationInvalid = false;
4298
+ this.offlineAuthenticationExpired = true;
4299
+ this.cancelRecovery(errorFromCode(401, "Session expired while offline"));
4300
+ this.rememberOfflineSessionOwner(token);
4301
+ if (this.token !== token) this.token$.next(token);
4302
+ if (this.status === "online") this.setConnectionStatus("offline");
4303
+ }
4304
+ markUnauthorized(token, preservePending = this.hasOwnedOfflineSession(token || this.token)) {
4245
4305
  if (token && token !== this.token) return;
4246
4306
  if (this.adoptStoredToken(token)) return;
4307
+ if (this.authenticationInvalid && this.unauthorizedLogout) return;
4247
4308
  this.authenticationInvalid = true;
4309
+ this.offlineAuthenticationExpired = false;
4248
4310
  this.cancelRecovery(errorFromCode(401, "Session expired"));
4249
- if (this.token != null) this.token$.next(null);
4311
+ if (this.token !== null) this.token$.next(null);
4250
4312
  this.setConnectionStatus("unauthorized");
4313
+ this.unauthorizedLogout ?? (this.unauthorizedLogout = Promise.resolve().then(() => this.finishUnauthorizedLogout(preservePending)).finally(() => {
4314
+ this.unauthorizedLogout = null;
4315
+ }));
4316
+ }
4317
+ async finishUnauthorizedLogout(preservePending = false) {
4318
+ var _a;
4319
+ if (typeof localStorage != "undefined") {
4320
+ localStorage.removeItem(this.localStorageKey);
4321
+ localStorage.removeItem("datalynk-user");
4322
+ if (!preservePending) localStorage.removeItem(this.offlineSessionOwnerKey);
4323
+ }
4324
+ if (!preservePending) this.offlineSessionOwner = null;
4325
+ for (const slice of this.sliceCache.values()) {
4326
+ try {
4327
+ slice.sync(false);
4328
+ } catch {
4329
+ }
4330
+ }
4331
+ await this.clearOfflineSessionCache(preservePending);
4332
+ if (typeof caches != "undefined") {
4333
+ try {
4334
+ await Promise.all([caches.delete("datalynk"), caches.delete("api-settings")]);
4335
+ } catch (error) {
4336
+ console.error("Unable to clear Datalynk service worker cache", error);
4337
+ }
4338
+ }
4339
+ if (typeof navigator != "undefined" && ((_a = navigator.serviceWorker) == null ? void 0 : _a.controller)) {
4340
+ navigator.serviceWorker.controller.postMessage({ token: null, clearCache: true });
4341
+ }
4342
+ if (typeof location != "undefined" && typeof location.reload == "function")
4343
+ location.reload();
4344
+ }
4345
+ async clearOfflineSessionCache(preservePending = false) {
4346
+ if (typeof indexedDB == "undefined") return;
4347
+ try {
4348
+ if (this.database) {
4349
+ const db = await this.database.connection;
4350
+ const stores = Array.from(db.objectStoreNames).filter((name) => !(preservePending && name === "pending"));
4351
+ if (!stores.length) return;
4352
+ await new Promise((resolve, reject) => {
4353
+ const tx = db.transaction(stores, "readwrite");
4354
+ stores.forEach((name) => tx.objectStore(name).clear());
4355
+ tx.oncomplete = () => resolve();
4356
+ tx.onerror = () => reject(tx.error);
4357
+ tx.onabort = () => reject(tx.error);
4358
+ });
4359
+ return;
4360
+ }
4361
+ await new Promise((resolve, reject) => {
4362
+ const request = indexedDB.open("datalynk");
4363
+ request.onerror = () => reject(request.error);
4364
+ request.onsuccess = () => {
4365
+ const db = request.result;
4366
+ const stores = Array.from(db.objectStoreNames).filter((name) => !(preservePending && name === "pending"));
4367
+ if (!stores.length) {
4368
+ db.close();
4369
+ resolve();
4370
+ return;
4371
+ }
4372
+ const tx = db.transaction(stores, "readwrite");
4373
+ stores.forEach((name) => tx.objectStore(name).clear());
4374
+ tx.oncomplete = () => {
4375
+ db.close();
4376
+ resolve();
4377
+ };
4378
+ tx.onerror = () => {
4379
+ const error = tx.error;
4380
+ db.close();
4381
+ reject(error);
4382
+ };
4383
+ tx.onabort = () => {
4384
+ const error = tx.error;
4385
+ db.close();
4386
+ reject(error);
4387
+ };
4388
+ };
4389
+ });
4390
+ } catch (error) {
4391
+ console.error("Unable to clear Datalynk offline session cache", error);
4392
+ }
4393
+ }
4394
+ tokenIdentity(token) {
4395
+ if (!token) return null;
4396
+ try {
4397
+ const payload = decodeJwt(token);
4398
+ const user = (payload == null ? void 0 : payload.uid) ?? (payload == null ? void 0 : payload.sub);
4399
+ return {
4400
+ realm: (payload == null ? void 0 : payload.realm) == null ? void 0 : String(payload.realm),
4401
+ user: user == null ? void 0 : String(user)
4402
+ };
4403
+ } catch {
4404
+ return null;
4405
+ }
4406
+ }
4407
+ readOfflineSessionOwner() {
4408
+ if (this.offlineSessionOwner) return this.offlineSessionOwner;
4409
+ if (typeof localStorage == "undefined") return null;
4410
+ try {
4411
+ const value = localStorage.getItem(this.offlineSessionOwnerKey);
4412
+ if (!value) return null;
4413
+ const owner = JSON.parse(value);
4414
+ if (!owner || typeof owner !== "object") return null;
4415
+ return this.offlineSessionOwner = owner;
4416
+ } catch {
4417
+ return null;
4418
+ }
4419
+ }
4420
+ ownersMatch(owner, identity2) {
4421
+ if (!identity2) return false;
4422
+ if (owner.realm != null && owner.realm !== identity2.realm) return false;
4423
+ if (owner.user != null && owner.user !== identity2.user) return false;
4424
+ return true;
4425
+ }
4426
+ rememberOfflineSessionOwner(token) {
4427
+ const identity2 = this.tokenIdentity(token);
4428
+ if (!identity2) return;
4429
+ const existing = this.readOfflineSessionOwner();
4430
+ if (existing && !this.ownersMatch(existing, identity2)) return;
4431
+ this.offlineSessionOwner = identity2;
4432
+ if (typeof localStorage != "undefined") {
4433
+ try {
4434
+ localStorage.setItem(this.offlineSessionOwnerKey, JSON.stringify(identity2));
4435
+ } catch {
4436
+ }
4437
+ }
4438
+ }
4439
+ ensureOfflineSessionOwner(token) {
4440
+ const existing = this.readOfflineSessionOwner();
4441
+ const identity2 = this.tokenIdentity(token);
4442
+ if (existing) return this.ownersMatch(existing, identity2);
4443
+ if (!identity2) return true;
4444
+ this.rememberOfflineSessionOwner(token);
4445
+ return true;
4446
+ }
4447
+ hasOwnedOfflineSession(token) {
4448
+ const owner = this.readOfflineSessionOwner();
4449
+ return !!owner && this.ownersMatch(owner, this.tokenIdentity(token));
4450
+ }
4451
+ clearOfflineSessionOwner() {
4452
+ this.offlineSessionOwner = null;
4453
+ if (typeof localStorage != "undefined") {
4454
+ try {
4455
+ localStorage.removeItem(this.offlineSessionOwnerKey);
4456
+ } catch {
4457
+ }
4458
+ }
4459
+ }
4460
+ /** Replay queued writes only after a matching authenticated session is online. */
4461
+ async replayPendingQueue() {
4462
+ if (!this.database || !this.online || this.authenticationInvalid || this.offlineAuthenticationExpired) return;
4463
+ if (this.replayingPending) {
4464
+ this.pendingReplayRequested = true;
4465
+ return;
4466
+ }
4467
+ const table = this.database.table("pending");
4468
+ const owner = this.readOfflineSessionOwner();
4469
+ if (owner && !this.ownersMatch(owner, this.tokenIdentity(this.token))) {
4470
+ if (this.token) console.warn("Datalynk offline queue is quarantined because it belongs to another authenticated user");
4471
+ return;
4472
+ }
4473
+ this.replayingPending = true;
4474
+ try {
4475
+ const keys = await table.getAllKeys();
4476
+ for (const key of keys) {
4477
+ if (!this.online || this.authenticationInvalid || this.offlineAuthenticationExpired) break;
4478
+ if (owner && !this.ownersMatch(owner, this.tokenIdentity(this.token))) break;
4479
+ const request = await table.get(key);
4480
+ if (request == null) continue;
4481
+ try {
4482
+ await this.request(request);
4483
+ await table.delete(key);
4484
+ } catch (error) {
4485
+ break;
4486
+ }
4487
+ }
4488
+ if (await table.count() === 0) this.clearOfflineSessionOwner();
4489
+ } finally {
4490
+ this.replayingPending = false;
4491
+ if (this.pendingReplayRequested) {
4492
+ this.pendingReplayRequested = false;
4493
+ if (this.online && !this.authenticationInvalid && !this.offlineAuthenticationExpired)
4494
+ void this.replayPendingQueue();
4495
+ }
4496
+ }
4251
4497
  }
4252
4498
  setConnectionStatus(status) {
4253
4499
  if (this.status !== status) this.status$.next(status);
4254
4500
  const online = status === "online";
4255
4501
  if (this.online !== online) this.online$.next(online);
4256
4502
  }
4257
- offlineBanner() {
4258
- if (this.options.offlineBanner === false || typeof document == "undefined") return;
4259
- if (this.status === "online") {
4260
- removeBanner("datalynk-offline-banner");
4261
- } else {
4262
- const message = this.status === "unauthorized" ? "⚠️ Your session expired, please sign in again" : this.status === "unavailable" ? "⚠️ Datalynk is currently unavailable" : "⚠️ You are offline, please reconnect to sync changes";
4263
- createBanner(message, {
4264
- id: "datalynk-offline-banner",
4265
- position: this.options.offlineBanner === "top" ? "top" : "bottom"
4266
- });
4267
- }
4268
- }
4269
4503
  startHeartbeat() {
4270
4504
  this.stopHeartbeat();
4271
4505
  this.heartbeat.interval = setInterval(() => this.checkConnection(), this.heartbeat.timeout / 2);
@@ -4375,6 +4609,8 @@ const _Api = class _Api {
4375
4609
  if (this.offline && typeof navigator != "undefined") {
4376
4610
  if (this.status === "unauthorized") return Promise.reject(errorFromCode(401, "Session expired"));
4377
4611
  if (options.offline) {
4612
+ if (!this.ensureOfflineSessionOwner(this.token))
4613
+ return Promise.reject(errorFromCode(409, "Offline queue belongs to another authenticated user"));
4378
4614
  (_b = (_a = this.database) == null ? void 0 : _a.table("pending")) == null ? void 0 : _b.add(data, key);
4379
4615
  return Promise.resolve();
4380
4616
  }
@@ -48,7 +48,7 @@ self.addEventListener('activate', (event) => {
48
48
 
49
49
  const settings = await getSettings();
50
50
  if(settings?.url) {
51
- api = new Api(settings.url, { ...(settings.options || {}), serviceWorker: false });
51
+ api = new Api(settings.url, { ...(settings.options || {}), serviceWorker: false, replayOfflineQueue: false });
52
52
  if(settings.token) api.token = settings.token;
53
53
  }
54
54
  })());
@@ -117,12 +117,17 @@ self.addEventListener('fetch', (event) => {
117
117
 
118
118
  self.addEventListener('message', async (event) => {
119
119
  if(event.data?.options) { // 🔧 Update settings
120
- api = new Api(event.data.options.url, { ...event.data.options, serviceWorker: false });
120
+ api = new Api(event.data.options.url, { ...event.data.options, serviceWorker: false, replayOfflineQueue: false });
121
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)
122
+ }
123
+ // `null` is an intentional token update during logout/expiry, so check for the
124
+ // property rather than truthiness.
125
+ if(event.data && Object.prototype.hasOwnProperty.call(event.data, 'token')) { // 🔑 Update token
126
+ const token = event.data.token ?? null;
127
+ if(api) api.token = token;
128
+ await setSettings({token});
129
+ }
130
+ if(event.data?.clearCache) { // 🧹 Clear cache
126
131
  await caches.delete(CACHE_NAME);
127
132
  }
128
133
  });
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.5.1",
5
+ "version": "1.5.2",
6
6
  "author": "Auxilium Group <devops@auxiliumgroup.com>",
7
7
  "private": false,
8
8
  "main": "./dist/index.cjs",