@steve31415/baselib 3.33.1 → 3.33.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.
@@ -217,6 +217,12 @@ export declare class SyncClient<B, C extends EventBody, S extends EventBody, Sum
217
217
  * the page counted as visible is counted (hidden pages are throttled by
218
218
  * design and their time is `hiddenMs`). */
219
219
  private observeStall;
220
+ /** The sleep half of `observeStall`, also run by every event-driven entry
221
+ * that starts, ends or splits the accounting (outage start and end, an
222
+ * `online` event, a hide): a wake can reach the page as one of those
223
+ * events before any timer runs, and the sleep must land on the right side
224
+ * of it (reader, 2026-10-11). */
225
+ private observeClockJump;
220
226
  private setTimer;
221
227
  private setRepeating;
222
228
  private isHidden;
@@ -382,20 +382,27 @@ export class SyncClient {
382
382
  * the page counted as visible is counted (hidden pages are throttled by
383
383
  * design and their time is `hiddenMs`). */
384
384
  observeStall(dueMono) {
385
+ this.observeClockJump();
385
386
  const mono = this.monoNow();
386
- const offset = this.now() - mono;
387
- const visible = !this.isHidden();
388
- if (visible && this.clockOffset !== null && offset - this.clockOffset >= STALL_MIN_MS) {
389
- this.counters.stalledMs += offset - this.clockOffset;
390
- }
391
- this.clockOffset = offset;
392
- if (visible && mono - dueMono >= STALL_MIN_MS) {
387
+ if (!this.isHidden() && mono - dueMono >= STALL_MIN_MS) {
393
388
  const from = Math.max(dueMono, this.stallCoveredUntil, this.visibleSinceMono);
394
389
  if (mono > from)
395
390
  this.counters.stalledMs += mono - from;
396
391
  this.stallCoveredUntil = mono;
397
392
  }
398
393
  }
394
+ /** The sleep half of `observeStall`, also run by every event-driven entry
395
+ * that starts, ends or splits the accounting (outage start and end, an
396
+ * `online` event, a hide): a wake can reach the page as one of those
397
+ * events before any timer runs, and the sleep must land on the right side
398
+ * of it (reader, 2026-10-11). */
399
+ observeClockJump() {
400
+ const offset = this.now() - this.monoNow();
401
+ if (!this.isHidden() && this.clockOffset !== null && offset - this.clockOffset >= STALL_MIN_MS) {
402
+ this.counters.stalledMs += offset - this.clockOffset;
403
+ }
404
+ this.clockOffset = offset;
405
+ }
399
406
  setTimer(callback, delayMs) {
400
407
  const dueMono = this.monoNow() + delayMs;
401
408
  return this.options.runtime.setTimeout(() => {
@@ -439,8 +446,9 @@ export class SyncClient {
439
446
  if (this.halted || this.disposed || this.hiddenSince !== null)
440
447
  return;
441
448
  this.finishReturnWindow();
442
- // A device that slept and then hid the page: the sleep counts as hidden.
443
- this.rebaselineClocks();
449
+ // A device that slept with the page visible and hid it on waking: the
450
+ // sleep counts as stalled, before the hidden spell starts.
451
+ this.observeClockJump();
444
452
  this.hiddenSince = this.now();
445
453
  this.spellCounters = this.snapshot();
446
454
  this.hiddenSocketFailed = false;
@@ -483,6 +491,7 @@ export class SyncClient {
483
491
  onOnline() {
484
492
  if (this.halted || this.disposed)
485
493
  return;
494
+ this.observeClockJump();
486
495
  if (this.isHidden()) {
487
496
  if (this.socket === null) {
488
497
  this.cancelBackgroundTick();
@@ -966,6 +975,10 @@ export class SyncClient {
966
975
  startOutage(closeCode, wasClean = false) {
967
976
  if (this.outageStartedAt !== null)
968
977
  return;
978
+ // A sleep or freeze that ended in this close happened before the outage
979
+ // began (durationMs starts now): keep it out of the outage's stalledMs.
980
+ this.observeClockJump();
981
+ this.stallCoveredUntil = Math.max(this.stallCoveredUntil, this.monoNow());
969
982
  this.outageStartedAt = this.options.runtime.now();
970
983
  this.outageEscalated = false;
971
984
  this.outageCloseCode = closeCode;
@@ -1300,6 +1313,7 @@ export class SyncClient {
1300
1313
  }
1301
1314
  onSyncEstablished() {
1302
1315
  if (this.outageStartedAt !== null) {
1316
+ this.observeClockJump();
1303
1317
  const durationMs = Math.max(0, this.options.runtime.now() - this.outageStartedAt);
1304
1318
  // Failures are only counted (and the ladder only runs) while visible,
1305
1319
  // and the count restarts on each return, so `attempts` is what the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steve31415/baselib",
3
- "version": "3.33.1",
3
+ "version": "3.33.2",
4
4
  "description": "Plasticine new-world shared platform library: logging, auth, service-to-service auth, db, HTTP, sync, app updates",
5
5
  "type": "module",
6
6
  "license": "MIT",