socket-function 1.1.48 → 1.1.49

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/index.d.ts CHANGED
@@ -1560,13 +1560,28 @@ declare module "socket-function/time/trueTimeShim" {
1560
1560
  fromTime: number;
1561
1561
  toTime: number;
1562
1562
  };
1563
+ export declare function computeTweenedOffset(components: {
1564
+ systemTime: number;
1565
+ fromOffset: number;
1566
+ toOffset: number;
1567
+ fromTime: number;
1568
+ }): number;
1563
1569
  export declare function getTimeComponents(): {
1564
1570
  systemTime: number;
1565
1571
  offset: number;
1566
1572
  };
1567
1573
  export declare function getTrueTime(): number;
1568
1574
  export declare function getTrueTimeOffset(): number;
1569
- export declare function getTrueTimeProof(): TimeOffsetProof | undefined;
1575
+ export type TrueTimeProof = {
1576
+ systemTime: number;
1577
+ fromOffset: number;
1578
+ toOffset: number;
1579
+ fromTime: number;
1580
+ toTime: number;
1581
+ offset: number;
1582
+ measurement?: TimeOffsetProof;
1583
+ };
1584
+ export declare function getTrueTimeProof(): TrueTimeProof | undefined;
1570
1585
  export declare function waitForFirstTimeSync(): Promise<void> | undefined;
1571
1586
  declare global {
1572
1587
  var TRUE_TIME_ALREADY_SHIMMED: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "socket-function",
3
- "version": "1.1.48",
3
+ "version": "1.1.49",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "dependencies": {
package/test.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { getTimeComponentsDetailed, waitForFirstTimeSync } from "./time/trueTimeShim";
1
+ import { computeTweenedOffset, getTimeComponentsDetailed, waitForFirstTimeSync } from "./time/trueTimeShim";
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
4
 
@@ -19,7 +19,6 @@ async function sampleMode() {
19
19
  toOffset: number;
20
20
  fromTime: number;
21
21
  toTime: number;
22
- fraction: number;
23
22
  };
24
23
 
25
24
  const samples: Sample[] = [];
@@ -28,11 +27,8 @@ async function sampleMode() {
28
27
  for (let i = 0; i < SAMPLE_COUNT; i++) {
29
28
  const detailed = getTimeComponentsDetailed();
30
29
 
31
- // Calculate smearing using the same systemTime
32
- const elapsed = detailed.systemTime - detailed.fromTime;
33
- const duration = detailed.toTime - detailed.fromTime;
34
- const fraction = duration > 0 ? Math.min(1, elapsed / duration) : 0;
35
- const offset = detailed.fromOffset + (detailed.toOffset - detailed.fromOffset) * fraction;
30
+ // Calculate the tween using the same systemTime
31
+ const offset = computeTweenedOffset(detailed);
36
32
 
37
33
  samples.push({
38
34
  id: TEST_RUN_ID,
@@ -42,7 +38,6 @@ async function sampleMode() {
42
38
  toOffset: detailed.toOffset,
43
39
  fromTime: detailed.fromTime,
44
40
  toTime: detailed.toTime,
45
- fraction,
46
41
  });
47
42
 
48
43
  if (SAMPLE_INTERVAL_MS > 0) {
@@ -88,7 +83,6 @@ async function verifyMode() {
88
83
  toOffset: number;
89
84
  fromTime: number;
90
85
  toTime: number;
91
- fraction: number;
92
86
  file: string;
93
87
  };
94
88
 
@@ -126,13 +120,13 @@ async function verifyMode() {
126
120
  console.error(` systemTime: ${prev.systemTime}`);
127
121
  console.error(` offset: ${prev.offset}`);
128
122
  console.error(` trueTime: ${prevTrueTime}`);
129
- console.error(` smearing: ${prev.fromOffset} -> ${prev.toOffset} (${(prev.fraction * 100).toFixed(2)}%)`);
123
+ console.error(` tween: ${prev.fromOffset} -> ${prev.toOffset}`);
130
124
  console.error(` timeWindow: ${prev.fromTime} -> ${prev.toTime}`);
131
125
  console.error(` Current [ID: ${sample.id}, file: ${sample.file}]:`);
132
126
  console.error(` systemTime: ${sample.systemTime}`);
133
127
  console.error(` offset: ${sample.offset}`);
134
128
  console.error(` trueTime: ${trueTime}`);
135
- console.error(` smearing: ${sample.fromOffset} -> ${sample.toOffset} (${(sample.fraction * 100).toFixed(2)}%)`);
129
+ console.error(` tween: ${sample.fromOffset} -> ${sample.toOffset}`);
136
130
  console.error(` timeWindow: ${sample.fromTime} -> ${sample.toTime}`);
137
131
  console.error(` Difference: ${trueTime - lastTrueTime}ms`);
138
132
  }
@@ -15,13 +15,28 @@ export declare function getTimeComponentsDetailed(): {
15
15
  fromTime: number;
16
16
  toTime: number;
17
17
  };
18
+ export declare function computeTweenedOffset(components: {
19
+ systemTime: number;
20
+ fromOffset: number;
21
+ toOffset: number;
22
+ fromTime: number;
23
+ }): number;
18
24
  export declare function getTimeComponents(): {
19
25
  systemTime: number;
20
26
  offset: number;
21
27
  };
22
28
  export declare function getTrueTime(): number;
23
29
  export declare function getTrueTimeOffset(): number;
24
- export declare function getTrueTimeProof(): TimeOffsetProof | undefined;
30
+ export type TrueTimeProof = {
31
+ systemTime: number;
32
+ fromOffset: number;
33
+ toOffset: number;
34
+ fromTime: number;
35
+ toTime: number;
36
+ offset: number;
37
+ measurement?: TimeOffsetProof;
38
+ };
39
+ export declare function getTrueTimeProof(): TrueTimeProof | undefined;
25
40
  export declare function waitForFirstTimeSync(): Promise<void> | undefined;
26
41
  declare global {
27
42
  var TRUE_TIME_ALREADY_SHIMMED: boolean;
@@ -22,6 +22,9 @@ const NTP_TIMEOUT = 1000 * 10;
22
22
  // the real time to catch up with it.
23
23
  const MINIMUM_TIME_RATE = 0.5;
24
24
 
25
+ // The offset tweens toward its target at this fraction of elapsed real time, then plateaus once it gets there. Must stay below 1 so output time keeps moving forward even while the offset is shrinking. Tweening across the whole transition window instead would mean a badly-off clock (many seconds) takes the entire window to converge, which breaks transactional consistency with other machines.
26
+ const OFFSET_TWEEN_RATE = 0.5;
27
+
25
28
  const THROW_ON_ERROR = false;
26
29
 
27
30
  // Hugely important as if we don't synchronize between processes, it means our logs are going to be confusing and out of order.
@@ -138,13 +141,24 @@ export function getTimeComponentsDetailed(): {
138
141
  }
139
142
  }
140
143
 
144
+ export function computeTweenedOffset(components: {
145
+ systemTime: number;
146
+ fromOffset: number;
147
+ toOffset: number;
148
+ fromTime: number;
149
+ }): number {
150
+ const elapsed = Math.max(0, components.systemTime - components.fromTime);
151
+ const delta = components.toOffset - components.fromOffset;
152
+ const maxChange = elapsed * OFFSET_TWEEN_RATE;
153
+ if (Math.abs(delta) <= maxChange) {
154
+ return components.toOffset;
155
+ }
156
+ return components.fromOffset + Math.sign(delta) * maxChange;
157
+ }
158
+
141
159
  export function getTimeComponents(): { systemTime: number; offset: number } {
142
160
  const detailed = getTimeComponentsDetailed();
143
- const elapsed = detailed.systemTime - detailed.fromTime;
144
- const duration = detailed.toTime - detailed.fromTime;
145
- const fraction = duration > 0 ? Math.min(1, elapsed / duration) : 0;
146
- const offset = detailed.fromOffset + (detailed.toOffset - detailed.fromOffset) * fraction;
147
- return { systemTime: detailed.systemTime, offset };
161
+ return { systemTime: detailed.systemTime, offset: computeTweenedOffset(detailed) };
148
162
  }
149
163
 
150
164
  export function getTrueTime() {
@@ -174,8 +188,30 @@ export function getTrueTimeOffset() {
174
188
  const { offset } = getTimeComponents();
175
189
  return offset;
176
190
  }
177
- export function getTrueTimeProof(): TimeOffsetProof | undefined {
178
- return cachedTimeOffsetData?.proof;
191
+ export type TrueTimeProof = {
192
+ systemTime: number;
193
+ // The tween endpoints the current offset is moving between. When offset !== toOffset we are still converging.
194
+ fromOffset: number;
195
+ toOffset: number;
196
+ fromTime: number;
197
+ toTime: number;
198
+ offset: number;
199
+ // The raw measurement that produced the newest offset. Undefined if the offset came from a caller-provided base that only returns numbers.
200
+ measurement?: TimeOffsetProof;
201
+ };
202
+ export function getTrueTimeProof(): TrueTimeProof | undefined {
203
+ const data = cachedTimeOffsetData;
204
+ if (!data) return undefined;
205
+ const detailed = getTimeComponentsDetailed();
206
+ return {
207
+ systemTime: detailed.systemTime,
208
+ fromOffset: detailed.fromOffset,
209
+ toOffset: detailed.toOffset,
210
+ fromTime: detailed.fromTime,
211
+ toTime: detailed.toTime,
212
+ offset: computeTweenedOffset(detailed),
213
+ measurement: data.proof,
214
+ };
179
215
  }
180
216
  export function waitForFirstTimeSync(): Promise<void> | undefined {
181
217
  if (didFirstTimeSync) return undefined;