@dash0/sdk-web 0.18.3 → 0.18.4
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/dist/dash0.iife.js +1 -1
- package/dist/dash0.iife.js.map +1 -1
- package/dist/dash0.js +1 -1
- package/dist/dash0.js.map +1 -1
- package/dist/dash0.umd.cjs +1 -1
- package/dist/dash0.umd.cjs.map +1 -1
- package/dist/modules/utils/time.js +11 -2
- package/dist/modules/utils/time_test.js +232 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/utils/time_test.d.ts +1 -0
- package/package.json +1 -1
- package/src/utils/time.ts +13 -2
- package/src/utils/time_test.ts +282 -0
|
@@ -4,10 +4,19 @@ export function now() {
|
|
|
4
4
|
}
|
|
5
5
|
export function nowNanos() {
|
|
6
6
|
const timeOrigin = getTimeOrigin();
|
|
7
|
+
const currentDate = new Date();
|
|
7
8
|
if (timeOrigin) {
|
|
8
|
-
|
|
9
|
+
const perfTime = perf.now() + timeOrigin;
|
|
10
|
+
const dateTime = currentDate.getTime();
|
|
11
|
+
// Only return perf time measurement if it hasn't significantly drifted (60s).
|
|
12
|
+
// Some browsers violate the performance api spec and do not tick the monotonic time while the OS sleeps or the browser hangs
|
|
13
|
+
// See https://bugzil.la/1709767
|
|
14
|
+
// See https://webkit.org/b/225610
|
|
15
|
+
if (Math.abs(perfTime - dateTime) < 60000) {
|
|
16
|
+
return String(perfTime * 1000000);
|
|
17
|
+
}
|
|
9
18
|
}
|
|
10
|
-
return toNanosTs(
|
|
19
|
+
return toNanosTs(currentDate);
|
|
11
20
|
}
|
|
12
21
|
export function toNanosTs(time) {
|
|
13
22
|
if (typeof time === "object") {
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { expect, describe, it, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { now, nowNanos, toNanosTs, getTimeOrigin, domHRTimestampToNanos } from "./time";
|
|
3
|
+
import * as globals from "./globals";
|
|
4
|
+
describe("now", () => {
|
|
5
|
+
it("returns the current time in milliseconds", () => {
|
|
6
|
+
const before = new Date().getTime();
|
|
7
|
+
const result = now();
|
|
8
|
+
const after = new Date().getTime();
|
|
9
|
+
expect(result).toBeGreaterThanOrEqual(before);
|
|
10
|
+
expect(result).toBeLessThanOrEqual(after);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
describe("toNanosTs", () => {
|
|
14
|
+
it("converts milliseconds to nanoseconds as string", () => {
|
|
15
|
+
const ms = 1609459200000; // 2021-01-01 00:00:00 UTC
|
|
16
|
+
const result = toNanosTs(ms);
|
|
17
|
+
expect(result).toBe("1609459200000000000");
|
|
18
|
+
});
|
|
19
|
+
it("converts Date object to nanoseconds as string", () => {
|
|
20
|
+
const date = new Date(1609459200000);
|
|
21
|
+
const result = toNanosTs(date);
|
|
22
|
+
expect(result).toBe("1609459200000000000");
|
|
23
|
+
});
|
|
24
|
+
it("handles zero timestamp", () => {
|
|
25
|
+
const result = toNanosTs(0);
|
|
26
|
+
expect(result).toBe("0000000");
|
|
27
|
+
});
|
|
28
|
+
it("preserves precision", () => {
|
|
29
|
+
const ms = 1234567890123;
|
|
30
|
+
const result = toNanosTs(ms);
|
|
31
|
+
expect(result).toBe("1234567890123000000");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe("getTimeOrigin", () => {
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
vi.restoreAllMocks();
|
|
37
|
+
});
|
|
38
|
+
it("returns timeOrigin when available", () => {
|
|
39
|
+
const mockPerf = {
|
|
40
|
+
timeOrigin: 1609459200000,
|
|
41
|
+
now: vi.fn(),
|
|
42
|
+
};
|
|
43
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
44
|
+
const result = getTimeOrigin();
|
|
45
|
+
expect(result).toBe(1609459200000);
|
|
46
|
+
});
|
|
47
|
+
it("falls back to timing.fetchStart when timeOrigin is not a number", () => {
|
|
48
|
+
const mockPerf = {
|
|
49
|
+
timing: {
|
|
50
|
+
fetchStart: 1609459200000,
|
|
51
|
+
},
|
|
52
|
+
now: vi.fn(),
|
|
53
|
+
};
|
|
54
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
55
|
+
const result = getTimeOrigin();
|
|
56
|
+
expect(result).toBe(1609459200000);
|
|
57
|
+
});
|
|
58
|
+
it("returns undefined when perf is not available", () => {
|
|
59
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(undefined);
|
|
60
|
+
const result = getTimeOrigin();
|
|
61
|
+
expect(result).toBeUndefined();
|
|
62
|
+
});
|
|
63
|
+
it("returns undefined when both timeOrigin and fetchStart are unavailable", () => {
|
|
64
|
+
const mockPerf = {
|
|
65
|
+
now: vi.fn(),
|
|
66
|
+
};
|
|
67
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
68
|
+
const result = getTimeOrigin();
|
|
69
|
+
expect(result).toBeUndefined();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("nowNanos", () => {
|
|
73
|
+
let dateNowSpy;
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
dateNowSpy = vi.spyOn(Date.prototype, "getTime");
|
|
76
|
+
});
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
vi.restoreAllMocks();
|
|
79
|
+
});
|
|
80
|
+
it("uses performance time when available and not drifted", () => {
|
|
81
|
+
const timeOrigin = 1609459200000;
|
|
82
|
+
const perfNow = 5000; // 5 seconds after time origin
|
|
83
|
+
const expectedTime = timeOrigin + perfNow; // 1609459205000
|
|
84
|
+
const mockPerf = {
|
|
85
|
+
timeOrigin,
|
|
86
|
+
now: vi.fn().mockReturnValue(perfNow),
|
|
87
|
+
};
|
|
88
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
89
|
+
dateNowSpy.mockReturnValue(expectedTime);
|
|
90
|
+
const result = nowNanos();
|
|
91
|
+
const expected = String(expectedTime * 1000000);
|
|
92
|
+
expect(result).toBe(expected);
|
|
93
|
+
});
|
|
94
|
+
it("falls back to Date when drift is exactly 60000ms (60 seconds)", () => {
|
|
95
|
+
const timeOrigin = 1609459200000;
|
|
96
|
+
const perfNow = 5000;
|
|
97
|
+
const perfTime = timeOrigin + perfNow; // 1609459205000
|
|
98
|
+
const dateTime = perfTime + 60000; // 60000ms drift
|
|
99
|
+
const mockPerf = {
|
|
100
|
+
timeOrigin,
|
|
101
|
+
now: vi.fn().mockReturnValue(perfNow),
|
|
102
|
+
};
|
|
103
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
104
|
+
dateNowSpy.mockReturnValue(dateTime);
|
|
105
|
+
const result = nowNanos();
|
|
106
|
+
const expected = String(dateTime) + "000000";
|
|
107
|
+
expect(result).toBe(expected);
|
|
108
|
+
});
|
|
109
|
+
it("falls back to Date when drift exceeds 60000ms (positive)", () => {
|
|
110
|
+
const timeOrigin = 1609459200000;
|
|
111
|
+
const perfNow = 5000;
|
|
112
|
+
const perfTime = timeOrigin + perfNow;
|
|
113
|
+
const dateTime = perfTime + 65000; // 65000ms drift (browser slept)
|
|
114
|
+
const mockPerf = {
|
|
115
|
+
timeOrigin,
|
|
116
|
+
now: vi.fn().mockReturnValue(perfNow),
|
|
117
|
+
};
|
|
118
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
119
|
+
dateNowSpy.mockReturnValue(dateTime);
|
|
120
|
+
const result = nowNanos();
|
|
121
|
+
const expected = String(dateTime) + "000000";
|
|
122
|
+
expect(result).toBe(expected);
|
|
123
|
+
});
|
|
124
|
+
it("falls back to Date when drift exceeds 60000ms (negative)", () => {
|
|
125
|
+
const timeOrigin = 1609459200000;
|
|
126
|
+
const perfNow = 5000;
|
|
127
|
+
const perfTime = timeOrigin + perfNow;
|
|
128
|
+
const dateTime = perfTime - 65000; // -65000ms drift (clock adjustment)
|
|
129
|
+
const mockPerf = {
|
|
130
|
+
timeOrigin,
|
|
131
|
+
now: vi.fn().mockReturnValue(perfNow),
|
|
132
|
+
};
|
|
133
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
134
|
+
dateNowSpy.mockReturnValue(dateTime);
|
|
135
|
+
const result = nowNanos();
|
|
136
|
+
const expected = String(dateTime) + "000000";
|
|
137
|
+
expect(result).toBe(expected);
|
|
138
|
+
});
|
|
139
|
+
it("uses performance time when drift is just under 60000ms (59999ms)", () => {
|
|
140
|
+
const timeOrigin = 1609459200000;
|
|
141
|
+
const perfNow = 5000;
|
|
142
|
+
const perfTime = timeOrigin + perfNow;
|
|
143
|
+
const dateTime = perfTime + 59999; // 59999ms drift (still acceptable)
|
|
144
|
+
const mockPerf = {
|
|
145
|
+
timeOrigin,
|
|
146
|
+
now: vi.fn().mockReturnValue(perfNow),
|
|
147
|
+
};
|
|
148
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
149
|
+
dateNowSpy.mockReturnValue(dateTime);
|
|
150
|
+
const result = nowNanos();
|
|
151
|
+
const expected = String(perfTime * 1000000);
|
|
152
|
+
expect(result).toBe(expected);
|
|
153
|
+
});
|
|
154
|
+
it("uses performance time when drift is just under -60000ms (-59999ms)", () => {
|
|
155
|
+
const timeOrigin = 1609459200000;
|
|
156
|
+
const perfNow = 5000;
|
|
157
|
+
const perfTime = timeOrigin + perfNow;
|
|
158
|
+
const dateTime = perfTime - 59999; // -59999ms drift (still acceptable)
|
|
159
|
+
const mockPerf = {
|
|
160
|
+
timeOrigin,
|
|
161
|
+
now: vi.fn().mockReturnValue(perfNow),
|
|
162
|
+
};
|
|
163
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
164
|
+
dateNowSpy.mockReturnValue(dateTime);
|
|
165
|
+
const result = nowNanos();
|
|
166
|
+
const expected = String(perfTime * 1000000);
|
|
167
|
+
expect(result).toBe(expected);
|
|
168
|
+
});
|
|
169
|
+
it("falls back to Date when timeOrigin is not available", () => {
|
|
170
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(undefined);
|
|
171
|
+
const dateTime = 1609459205000;
|
|
172
|
+
dateNowSpy.mockReturnValue(dateTime);
|
|
173
|
+
const result = nowNanos();
|
|
174
|
+
const expected = String(dateTime) + "000000";
|
|
175
|
+
expect(result).toBe(expected);
|
|
176
|
+
});
|
|
177
|
+
it("falls back to Date when timeOrigin is not a number", () => {
|
|
178
|
+
const mockPerf = {
|
|
179
|
+
timeOrigin: undefined,
|
|
180
|
+
now: vi.fn().mockReturnValue(5000),
|
|
181
|
+
timing: {}, // no fetchStart either
|
|
182
|
+
};
|
|
183
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
184
|
+
const dateTime = 1609459205000;
|
|
185
|
+
dateNowSpy.mockReturnValue(dateTime);
|
|
186
|
+
const result = nowNanos();
|
|
187
|
+
const expected = String(dateTime) + "000000";
|
|
188
|
+
expect(result).toBe(expected);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
describe("domHRTimestampToNanos", () => {
|
|
192
|
+
afterEach(() => {
|
|
193
|
+
vi.restoreAllMocks();
|
|
194
|
+
});
|
|
195
|
+
it("converts DOM high resolution timestamp to nanoseconds", () => {
|
|
196
|
+
const timeOrigin = 1609459200000;
|
|
197
|
+
const hrTimestamp = 5000.123; // 5.000123 seconds after time origin
|
|
198
|
+
const mockPerf = {
|
|
199
|
+
timeOrigin,
|
|
200
|
+
now: vi.fn(),
|
|
201
|
+
};
|
|
202
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
203
|
+
const result = domHRTimestampToNanos(hrTimestamp);
|
|
204
|
+
const expectedMs = timeOrigin + hrTimestamp;
|
|
205
|
+
const expectedNanos = String(Math.round(expectedMs * 1000000));
|
|
206
|
+
expect(result).toBe(expectedNanos);
|
|
207
|
+
});
|
|
208
|
+
it("rounds to nearest nanosecond", () => {
|
|
209
|
+
const timeOrigin = 1000;
|
|
210
|
+
const hrTimestamp = 0.0000001; // Very small fraction
|
|
211
|
+
const mockPerf = {
|
|
212
|
+
timeOrigin,
|
|
213
|
+
now: vi.fn(),
|
|
214
|
+
};
|
|
215
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
216
|
+
const result = domHRTimestampToNanos(hrTimestamp);
|
|
217
|
+
const expectedMs = timeOrigin + hrTimestamp;
|
|
218
|
+
const expectedNanos = String(Math.round(expectedMs * 1000000));
|
|
219
|
+
expect(result).toBe(expectedNanos);
|
|
220
|
+
});
|
|
221
|
+
it("handles zero timestamp", () => {
|
|
222
|
+
const timeOrigin = 1609459200000;
|
|
223
|
+
const mockPerf = {
|
|
224
|
+
timeOrigin,
|
|
225
|
+
now: vi.fn(),
|
|
226
|
+
};
|
|
227
|
+
vi.spyOn(globals, "perf", "get").mockReturnValue(mockPerf);
|
|
228
|
+
const result = domHRTimestampToNanos(0);
|
|
229
|
+
const expectedNanos = String(Math.round(timeOrigin * 1000000));
|
|
230
|
+
expect(result).toBe(expectedNanos);
|
|
231
|
+
});
|
|
232
|
+
});
|