@hydranium/protocol 1.0.0-next.270 → 1.0.0-next.272

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/lib/clock.d.ts CHANGED
@@ -12,9 +12,11 @@ import { Disposable } from './util';
12
12
  * there is no `start` — you create it at the moment timing begins.
13
13
  *
14
14
  * Backed by a monotonic source (`performance.now`), so it is sub-millisecond
15
- * precise and immune to wall-clock jumps (NTP/DST adjustments can make a
16
- * `Date.now` delta go backwards; a stopwatch cannot). Use this for durations
17
- * and {@link Clock.now} for "what time is it" (timestamps, TTLs).
15
+ * precise and immune to wall-clock steps (a time sync or a manual change can
16
+ * make a `Date.now` delta go backwards; a stopwatch cannot). In Node on Linux
17
+ * it also leaves out time the system spends suspended, so a duration spanning
18
+ * a suspend reads as the awake time only. Use this for every duration, a TTL
19
+ * included, and {@link Clock.now} for "what time is it".
18
20
  *
19
21
  * It holds no scheduled resource and is therefore NOT a {@link Disposable} —
20
22
  * the disposable thing is a {@link Clock.setTimer}. Reads are lazy, so a fake
@@ -42,16 +44,16 @@ export interface Stopwatch {
42
44
  * testable instead of waiting on real wall-clock. The {@link SystemClock}
43
45
  * default delegates to the platform; tests bind `makeFakeClock`.
44
46
  *
45
- * Two distinct readings on purpose: {@link now} is wall-clock (for timestamps
46
- * and TTLs that compare against a file's mtime), while {@link stopwatch}
47
- * measures durations off a monotonic source. They are different clocks; do not
48
- * subtract two {@link now} readings to measure a duration.
47
+ * Two distinct readings on purpose: {@link now} is wall-clock, for a
48
+ * timestamp, while {@link stopwatch} measures durations off a monotonic
49
+ * source. They are different clocks; do not subtract two {@link now} readings
50
+ * to measure a duration. Framework code reads time only through a `Clock`, so
51
+ * a platform workaround lives in one place.
49
52
  *
50
53
  * A cross-head contract, so it carries no platform or DI-framework coupling.
51
54
  *
52
- * Note: the runtime health monitors (event-loop, memory) intentionally keep
53
- * the real wall-clock rather than taking a `Clock` — faking the time they
54
- * measure would invert their meaning.
55
+ * Note: code that measures real CPU, heap or event-loop time never takes the
56
+ * bound `Clock`: faking the time it measures would invert its meaning.
55
57
  */
56
58
  export interface Clock {
57
59
  /** Current wall-clock time in milliseconds (the `Date.now` reading). */
@@ -1 +1 @@
1
- {"version":3,"file":"clock.d.ts","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,SAAS;IACvB,mFAAmF;IACnF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,GAAG,IAAI,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,IAAI,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,KAAK;IACnB,wEAAwE;IACxE,GAAG,IAAI,MAAM,CAAC;IACd;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAAC;IACvD,yDAAyD;IACzD,SAAS,IAAI,SAAS,CAAC;IACvB;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACxC;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,OAAO,SAAS,CAAC,CAAC;CAC/E;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,eAA6C,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,KAAK,CAAC,CAAC;IACrB;;;;;OAKG;IACH,MAAM,EAAE,CAAC,CAAC;IACV;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC;CACpB;AAyCD;;;;;;;;GAQG;AACH,qBAAa,WAAY,YAAW,KAAK;IACtC,GAAG,IAAI,MAAM,CAEZ;IAED,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAIrD;IAED,SAAS,IAAI,SAAS,CAErB;IAED,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAUxC;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,OAAO,SAAS,CAAC,CAgB3E;CACH"}
1
+ {"version":3,"file":"clock.d.ts","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,SAAS;IACvB,mFAAmF;IACnF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,GAAG,IAAI,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,IAAI,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,KAAK;IACnB,wEAAwE;IACxE,GAAG,IAAI,MAAM,CAAC;IACd;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAAC;IACvD,yDAAyD;IACzD,SAAS,IAAI,SAAS,CAAC;IACvB;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACxC;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,OAAO,SAAS,CAAC,CAAC;CAC/E;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,eAA6C,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,KAAK,CAAC,CAAC;IACrB;;;;;OAKG;IACH,MAAM,EAAE,CAAC,CAAC;IACV;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC;CACpB;AAyCD;;;;;;;;GAQG;AACH,qBAAa,WAAY,YAAW,KAAK;IACtC,GAAG,IAAI,MAAM,CAEZ;IAED,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAIrD;IAED,SAAS,IAAI,SAAS,CAErB;IAED,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAUxC;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,OAAO,SAAS,CAAC,CAgB3E;CACH"}
package/lib/clock.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"clock.js","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"clock.js","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
- import { type Clock } from './clock';
9
+ import { type Clock, type Stopwatch } from './clock';
10
10
  import { ObservableValue, type MaybeObservableValue } from './observable-value';
11
11
  import { type Disposable } from './util';
12
12
  /** Construction options for {@link Debouncer}. */
@@ -52,8 +52,8 @@ export declare class Debouncer implements Disposable {
52
52
  protected readonly delay: ObservableValue<number>;
53
53
  protected readonly maxWaitMs?: number;
54
54
  protected timer?: Disposable;
55
- /** Virtual time of the first {@link schedule} of the current pending window; drives {@link DebouncerOptions.maxWaitMs}. */
56
- protected firstScheduledAt?: number;
55
+ /** Running since the first {@link schedule} of the current pending window; drives {@link DebouncerOptions.maxWaitMs}. */
56
+ protected sinceFirstSchedule?: Stopwatch;
57
57
  constructor(clock: Clock, fn: () => void, options: DebouncerOptions);
58
58
  /** True while a fire is armed (scheduled, not yet fired or cancelled). */
59
59
  get pending(): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"debouncer.d.ts","sourceRoot":"","sources":["../src/debouncer.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC,kDAAkD;AAClD,MAAM,WAAW,gBAAgB;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC/C;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,SAAU,YAAW,UAAU;IAQtC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK;IAC/B,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,IAAI;IARpC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IACtC,SAAS,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC7B,2HAA2H;IAC3H,SAAS,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAEpC,YACsB,KAAK,EAAE,KAAK,EACZ,EAAE,EAAE,MAAM,IAAI,EACjC,OAAO,EAAE,gBAAgB,EAI3B;IAED,0EAA0E;IAC1E,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;;OAKG;IACH,QAAQ,IAAI,IAAI,CAYf;IAED,wFAAwF;IACxF,KAAK,IAAI,IAAI,CAIZ;IAED,sDAAsD;IACtD,MAAM,IAAI,IAAI,CAIb;IAED,2CAA2C;IAC3C,OAAO,IAAI,IAAI,CAEd;IAED,SAAS,CAAC,IAAI,IAAI,IAAI,CAKrB;CACH"}
1
+ {"version":3,"file":"debouncer.d.ts","sourceRoot":"","sources":["../src/debouncer.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC,kDAAkD;AAClD,MAAM,WAAW,gBAAgB;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC/C;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,SAAU,YAAW,UAAU;IAQtC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK;IAC/B,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,IAAI;IARpC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IACtC,SAAS,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC7B,yHAAyH;IACzH,SAAS,CAAC,kBAAkB,CAAC,EAAE,SAAS,CAAC;IAEzC,YACsB,KAAK,EAAE,KAAK,EACZ,EAAE,EAAE,MAAM,IAAI,EACjC,OAAO,EAAE,gBAAgB,EAI3B;IAED,0EAA0E;IAC1E,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;;OAKG;IACH,QAAQ,IAAI,IAAI,CASf;IAED,wFAAwF;IACxF,KAAK,IAAI,IAAI,CAIZ;IAED,sDAAsD;IACtD,MAAM,IAAI,IAAI,CAIb;IAED,2CAA2C;IAC3C,OAAO,IAAI,IAAI,CAEd;IAED,SAAS,CAAC,IAAI,IAAI,IAAI,CAKrB;CACH"}
package/lib/debouncer.js CHANGED
@@ -33,8 +33,8 @@ class Debouncer {
33
33
  delay;
34
34
  maxWaitMs;
35
35
  timer;
36
- /** Virtual time of the first {@link schedule} of the current pending window; drives {@link DebouncerOptions.maxWaitMs}. */
37
- firstScheduledAt;
36
+ /** Running since the first {@link schedule} of the current pending window; drives {@link DebouncerOptions.maxWaitMs}. */
37
+ sinceFirstSchedule;
38
38
  constructor(clock, fn, options) {
39
39
  this.clock = clock;
40
40
  this.fn = fn;
@@ -52,13 +52,10 @@ class Debouncer {
52
52
  * elapses since the first pending call.
53
53
  */
54
54
  schedule() {
55
- const now = this.clock.now();
56
- if (this.firstScheduledAt === undefined) {
57
- this.firstScheduledAt = now;
58
- }
55
+ this.sinceFirstSchedule ??= this.clock.stopwatch();
59
56
  let wait = Math.max(0, this.delay.value);
60
57
  if (this.maxWaitMs !== undefined) {
61
- const untilCeiling = this.firstScheduledAt + this.maxWaitMs - now;
58
+ const untilCeiling = this.maxWaitMs - this.sinceFirstSchedule.elapsedMs;
62
59
  wait = Math.min(wait, Math.max(0, untilCeiling));
63
60
  }
64
61
  this.timer?.dispose();
@@ -74,7 +71,7 @@ class Debouncer {
74
71
  cancel() {
75
72
  this.timer?.dispose();
76
73
  this.timer = undefined;
77
- this.firstScheduledAt = undefined;
74
+ this.sinceFirstSchedule = undefined;
78
75
  }
79
76
  /** Cancel any pending fire and release. */
80
77
  dispose() {
@@ -83,7 +80,7 @@ class Debouncer {
83
80
  fire() {
84
81
  this.timer?.dispose();
85
82
  this.timer = undefined;
86
- this.firstScheduledAt = undefined;
83
+ this.sinceFirstSchedule = undefined;
87
84
  this.fn();
88
85
  }
89
86
  }
@@ -1 +1 @@
1
- {"version":3,"file":"debouncer.js","sourceRoot":"","sources":["../src/debouncer.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AAGlF,yDAAgF;AAwBhF;;;;;;;;;;;;;;;;GAgBG;AACH;IAQyB,KAAK;IACL,EAAE;IARL,KAAK,CAA0B;IAC/B,SAAS,CAAU;IAC5B,KAAK,CAAc;IAC7B,2HAA2H;IACjH,gBAAgB,CAAU;IAEpC,YACsB,KAAY,EACZ,EAAc,EACjC,OAAyB;qBAFN,KAAK;kBACL,EAAE;QAGrB,IAAI,CAAC,KAAK,GAAG,kCAAe,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACtC,CAAC;IAED,0EAA0E;IAC1E,IAAI,OAAO;QACR,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC;IACnC,CAAC;IAED;;;;;OAKG;IACH,QAAQ;QACL,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAC7B,IAAI,IAAI,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YACvC,IAAI,CAAC,gBAAgB,GAAG,GAAG,CAAC;QAC/B,CAAC;QACD,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;YAClE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;IAC7D,CAAC;IAED,wFAAwF;IACxF,KAAK;QACF,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;QACf,CAAC;IACJ,CAAC;IAED,sDAAsD;IACtD,MAAM;QACH,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;IACrC,CAAC;IAED,2CAA2C;IAC3C,OAAO;QACJ,IAAI,CAAC,MAAM,EAAE,CAAC;IACjB,CAAC;IAES,IAAI;QACX,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QAClC,IAAI,CAAC,EAAE,EAAE,CAAC;IACb,CAAC;CACH"}
1
+ {"version":3,"file":"debouncer.js","sourceRoot":"","sources":["../src/debouncer.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AAGlF,yDAAgF;AAwBhF;;;;;;;;;;;;;;;;GAgBG;AACH;IAQyB,KAAK;IACL,EAAE;IARL,KAAK,CAA0B;IAC/B,SAAS,CAAU;IAC5B,KAAK,CAAc;IAC7B,yHAAyH;IAC/G,kBAAkB,CAAa;IAEzC,YACsB,KAAY,EACZ,EAAc,EACjC,OAAyB;qBAFN,KAAK;kBACL,EAAE;QAGrB,IAAI,CAAC,KAAK,GAAG,kCAAe,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACtC,CAAC;IAED,0EAA0E;IAC1E,IAAI,OAAO;QACR,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC;IACnC,CAAC;IAED;;;;;OAKG;IACH,QAAQ;QACL,IAAI,CAAC,kBAAkB,KAAK,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QACnD,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC;YACxE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;IAC7D,CAAC;IAED,wFAAwF;IACxF,KAAK;QACF,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;QACf,CAAC;IACJ,CAAC;IAED,sDAAsD;IACtD,MAAM;QACH,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;IACvC,CAAC;IAED,2CAA2C;IAC3C,OAAO;QACJ,IAAI,CAAC,MAAM,EAAE,CAAC;IACjB,CAAC;IAES,IAAI;QACX,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;QACpC,IAAI,CAAC,EAAE,EAAE,CAAC;IACb,CAAC;CACH"}
@@ -19,6 +19,12 @@ export interface FakeClock extends Clock {
19
19
  * chain settle before advancing.
20
20
  */
21
21
  advance(ms: number): void;
22
+ /**
23
+ * Move `now()` by `ms`, backwards when negative, leaving stopwatches and
24
+ * timers where they are: the wall-clock step a time sync or a manual change makes,
25
+ * which a duration taken from two `now()` readings gets wrong.
26
+ */
27
+ jumpWallClock(ms: number): void;
22
28
  /** How many {@link Clock.setTimer} timers have neither fired nor been disposed. */
23
29
  pendingTimers(): number;
24
30
  }
@@ -28,7 +34,8 @@ export interface FakeClock extends Clock {
28
34
  * (`Date.now` for `now()`, `performance.now()` for the stopwatch); the fake
29
35
  * collapses them onto ONE virtual time axis, so a single
30
36
  * {@link FakeClock.advance} drives `now()`, every live stopwatch's elapsed, and
31
- * any due timer together.
37
+ * any due timer together; only {@link FakeClock.jumpWallClock} pulls `now()`
38
+ * off that axis.
32
39
  *
33
40
  * Server-free and DI-free — lives in `@hydranium/protocol/testing` so every
34
41
  * head can bind it without pulling in a server package.
@@ -1 +1 @@
1
- {"version":3,"file":"fake-clock.d.ts","sourceRoot":"","sources":["../../src/testing/fake-clock.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,KAAK,EAA+B,MAAM,UAAU,CAAC;AAGnE,2FAA2F;AAC3F,MAAM,WAAW,SAAU,SAAQ,KAAK;IACrC;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,mFAAmF;IACnF,aAAa,IAAI,MAAM,CAAC;CAC1B;AASD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,SAAS,CAEvE"}
1
+ {"version":3,"file":"fake-clock.d.ts","sourceRoot":"","sources":["../../src/testing/fake-clock.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,KAAK,EAA+B,MAAM,UAAU,CAAC;AAGnE,2FAA2F;AAC3F,MAAM,WAAW,SAAU,SAAQ,KAAK;IACrC;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;OAIG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mFAAmF;IACnF,aAAa,IAAI,MAAM,CAAC;CAC1B;AASD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,SAAS,CAEvE"}
@@ -17,7 +17,8 @@ const util_1 = require("../util");
17
17
  * (`Date.now` for `now()`, `performance.now()` for the stopwatch); the fake
18
18
  * collapses them onto ONE virtual time axis, so a single
19
19
  * {@link FakeClock.advance} drives `now()`, every live stopwatch's elapsed, and
20
- * any due timer together.
20
+ * any due timer together; only {@link FakeClock.jumpWallClock} pulls `now()`
21
+ * off that axis.
21
22
  *
22
23
  * Server-free and DI-free — lives in `@hydranium/protocol/testing` so every
23
24
  * head can bind it without pulling in a server package.
@@ -36,12 +37,17 @@ function makeFakeClock(options = {}) {
36
37
  class VirtualClock extends clock_1.SystemClock {
37
38
  current;
38
39
  timers = [];
40
+ /** What {@link jumpWallClock} has added to `now()`. */
41
+ wallClockOffset = 0;
39
42
  constructor(current) {
40
43
  super();
41
44
  this.current = current;
42
45
  }
43
46
  now() {
44
- return this.current;
47
+ return this.current + this.wallClockOffset;
48
+ }
49
+ jumpWallClock(ms) {
50
+ this.wallClockOffset += ms;
45
51
  }
46
52
  setTimer(callback, ms) {
47
53
  const timer = { at: this.current + ms, callback, disposed: false, fired: false };
@@ -1 +1 @@
1
- {"version":3,"file":"fake-clock.js","sourceRoot":"","sources":["../../src/testing/fake-clock.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AAElF,oCAAmE;AACnE,kCAAqC;AAyBrC;;;;;;;;;;;;;;GAcG;AACH,uBAA8B,OAAO,GAAqB,EAAE;IACzD,OAAO,IAAI,YAAY,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,YAAa,SAAQ,mBAAW;IAGb,OAAO;IAFV,MAAM,GAAgB,EAAE,CAAC;IAE5C,YAAsB,OAAe;QAClC,KAAK,EAAE,CAAC;uBADW,OAAO;IAE7B,CAAC;IAEQ,GAAG;QACT,OAAO,IAAI,CAAC,OAAO,CAAC;IACvB,CAAC;IAEQ,QAAQ,CAAC,QAAoB,EAAE,EAAU;QAC/C,MAAM,KAAK,GAAc,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,GAAG,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC5F,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,OAAO,iBAAU,CAAC,MAAM,CAAC,GAAG,EAAE;YAC3B,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;QACzB,CAAC,CAAC,CAAC;IACN,CAAC;IAEQ,SAAS;QACf,MAAM,IAAI,GAAG,GAAW,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC;QACrB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,OAA2B,CAAC;QAChC,OAAO;YACJ,IAAI,SAAS;gBACV,OAAO,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC;YACtC,CAAC;YACD,GAAG;gBACA,MAAM,EAAE,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC;gBAC7B,MAAM,KAAK,GAAG,EAAE,GAAG,OAAO,CAAC;gBAC3B,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,KAAK,CAAC;YAChB,CAAC;YACD,IAAI;gBACD,OAAO,KAAK,IAAI,EAAE,CAAC;gBACnB,OAAO,OAAO,GAAG,KAAK,CAAC;YAC1B,CAAC;SACH,CAAC;IACL,CAAC;IAED,aAAa;QACV,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;IAC9E,CAAC;IAED,OAAO,CAAC,EAAU;QACf,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QACjC,sEAAsE;QACtE,gEAAgE;QAChE,SAAS,CAAC;YACP,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM;iBACpB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,EAAE,IAAI,MAAM,CAAC;iBACtE,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACjD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACT,MAAM;YACT,CAAC;YACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnB,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACzB,CAAC;CACH"}
1
+ {"version":3,"file":"fake-clock.js","sourceRoot":"","sources":["../../src/testing/fake-clock.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydranium/protocol",
3
- "version": "1.0.0-next.270",
3
+ "version": "1.0.0-next.272",
4
4
  "description": "Generic, language-agnostic types, constants, and pure utilities for the hydranium framework.",
5
5
  "keywords": [
6
6
  "hydranium",
package/src/clock.ts CHANGED
@@ -14,9 +14,11 @@ import { Disposable } from './util';
14
14
  * there is no `start` — you create it at the moment timing begins.
15
15
  *
16
16
  * Backed by a monotonic source (`performance.now`), so it is sub-millisecond
17
- * precise and immune to wall-clock jumps (NTP/DST adjustments can make a
18
- * `Date.now` delta go backwards; a stopwatch cannot). Use this for durations
19
- * and {@link Clock.now} for "what time is it" (timestamps, TTLs).
17
+ * precise and immune to wall-clock steps (a time sync or a manual change can
18
+ * make a `Date.now` delta go backwards; a stopwatch cannot). In Node on Linux
19
+ * it also leaves out time the system spends suspended, so a duration spanning
20
+ * a suspend reads as the awake time only. Use this for every duration, a TTL
21
+ * included, and {@link Clock.now} for "what time is it".
20
22
  *
21
23
  * It holds no scheduled resource and is therefore NOT a {@link Disposable} —
22
24
  * the disposable thing is a {@link Clock.setTimer}. Reads are lazy, so a fake
@@ -45,16 +47,16 @@ export interface Stopwatch {
45
47
  * testable instead of waiting on real wall-clock. The {@link SystemClock}
46
48
  * default delegates to the platform; tests bind `makeFakeClock`.
47
49
  *
48
- * Two distinct readings on purpose: {@link now} is wall-clock (for timestamps
49
- * and TTLs that compare against a file's mtime), while {@link stopwatch}
50
- * measures durations off a monotonic source. They are different clocks; do not
51
- * subtract two {@link now} readings to measure a duration.
50
+ * Two distinct readings on purpose: {@link now} is wall-clock, for a
51
+ * timestamp, while {@link stopwatch} measures durations off a monotonic
52
+ * source. They are different clocks; do not subtract two {@link now} readings
53
+ * to measure a duration. Framework code reads time only through a `Clock`, so
54
+ * a platform workaround lives in one place.
52
55
  *
53
56
  * A cross-head contract, so it carries no platform or DI-framework coupling.
54
57
  *
55
- * Note: the runtime health monitors (event-loop, memory) intentionally keep
56
- * the real wall-clock rather than taking a `Clock` — faking the time they
57
- * measure would invert their meaning.
58
+ * Note: code that measures real CPU, heap or event-loop time never takes the
59
+ * bound `Clock`: faking the time it measures would invert its meaning.
58
60
  */
59
61
  export interface Clock {
60
62
  /** Current wall-clock time in milliseconds (the `Date.now` reading). */
package/src/debouncer.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { type Clock } from './clock';
10
+ import { type Clock, type Stopwatch } from './clock';
11
11
  import { ObservableValue, type MaybeObservableValue } from './observable-value';
12
12
  import { type Disposable } from './util';
13
13
 
@@ -53,8 +53,8 @@ export class Debouncer implements Disposable {
53
53
  protected readonly delay: ObservableValue<number>;
54
54
  protected readonly maxWaitMs?: number;
55
55
  protected timer?: Disposable;
56
- /** Virtual time of the first {@link schedule} of the current pending window; drives {@link DebouncerOptions.maxWaitMs}. */
57
- protected firstScheduledAt?: number;
56
+ /** Running since the first {@link schedule} of the current pending window; drives {@link DebouncerOptions.maxWaitMs}. */
57
+ protected sinceFirstSchedule?: Stopwatch;
58
58
 
59
59
  constructor(
60
60
  protected readonly clock: Clock,
@@ -77,13 +77,10 @@ export class Debouncer implements Disposable {
77
77
  * elapses since the first pending call.
78
78
  */
79
79
  schedule(): void {
80
- const now = this.clock.now();
81
- if (this.firstScheduledAt === undefined) {
82
- this.firstScheduledAt = now;
83
- }
80
+ this.sinceFirstSchedule ??= this.clock.stopwatch();
84
81
  let wait = Math.max(0, this.delay.value);
85
82
  if (this.maxWaitMs !== undefined) {
86
- const untilCeiling = this.firstScheduledAt + this.maxWaitMs - now;
83
+ const untilCeiling = this.maxWaitMs - this.sinceFirstSchedule.elapsedMs;
87
84
  wait = Math.min(wait, Math.max(0, untilCeiling));
88
85
  }
89
86
  this.timer?.dispose();
@@ -101,7 +98,7 @@ export class Debouncer implements Disposable {
101
98
  cancel(): void {
102
99
  this.timer?.dispose();
103
100
  this.timer = undefined;
104
- this.firstScheduledAt = undefined;
101
+ this.sinceFirstSchedule = undefined;
105
102
  }
106
103
 
107
104
  /** Cancel any pending fire and release. */
@@ -112,7 +109,7 @@ export class Debouncer implements Disposable {
112
109
  protected fire(): void {
113
110
  this.timer?.dispose();
114
111
  this.timer = undefined;
115
- this.firstScheduledAt = undefined;
112
+ this.sinceFirstSchedule = undefined;
116
113
  this.fn();
117
114
  }
118
115
  }
@@ -22,6 +22,12 @@ export interface FakeClock extends Clock {
22
22
  * chain settle before advancing.
23
23
  */
24
24
  advance(ms: number): void;
25
+ /**
26
+ * Move `now()` by `ms`, backwards when negative, leaving stopwatches and
27
+ * timers where they are: the wall-clock step a time sync or a manual change makes,
28
+ * which a duration taken from two `now()` readings gets wrong.
29
+ */
30
+ jumpWallClock(ms: number): void;
25
31
  /** How many {@link Clock.setTimer} timers have neither fired nor been disposed. */
26
32
  pendingTimers(): number;
27
33
  }
@@ -39,7 +45,8 @@ interface FakeTimer {
39
45
  * (`Date.now` for `now()`, `performance.now()` for the stopwatch); the fake
40
46
  * collapses them onto ONE virtual time axis, so a single
41
47
  * {@link FakeClock.advance} drives `now()`, every live stopwatch's elapsed, and
42
- * any due timer together.
48
+ * any due timer together; only {@link FakeClock.jumpWallClock} pulls `now()`
49
+ * off that axis.
43
50
  *
44
51
  * Server-free and DI-free — lives in `@hydranium/protocol/testing` so every
45
52
  * head can bind it without pulling in a server package.
@@ -58,13 +65,19 @@ export function makeFakeClock(options: { now?: number } = {}): FakeClock {
58
65
  */
59
66
  class VirtualClock extends SystemClock implements FakeClock {
60
67
  protected readonly timers: FakeTimer[] = [];
68
+ /** What {@link jumpWallClock} has added to `now()`. */
69
+ protected wallClockOffset = 0;
61
70
 
62
71
  constructor(protected current: number) {
63
72
  super();
64
73
  }
65
74
 
66
75
  override now(): number {
67
- return this.current;
76
+ return this.current + this.wallClockOffset;
77
+ }
78
+
79
+ jumpWallClock(ms: number): void {
80
+ this.wallClockOffset += ms;
68
81
  }
69
82
 
70
83
  override setTimer(callback: () => void, ms: number): Disposable {