@provex/utils 1.0.16 → 1.2.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/chain.d.ts +446 -0
- package/dist/chain.js +98 -0
- package/dist/chain.test.js +112 -0
- package/dist/contracts.d.ts +206 -0
- package/dist/contracts.js +292 -0
- package/dist/contracts.test.js +243 -0
- package/dist/conversionRates.d.ts +215 -0
- package/dist/{src/conversionRates.js → conversionRates.js} +74 -4
- package/dist/conversionRates.test.js +473 -0
- package/dist/currencies.d.ts +248 -0
- package/dist/{src/currencies.js → currencies.js} +119 -38
- package/dist/currencies.test.js +343 -0
- package/dist/data/countries.json +1099 -0
- package/dist/deposit.d.ts +69 -0
- package/dist/deposit.js +52 -0
- package/dist/depositKeys.d.ts +154 -0
- package/dist/depositKeys.js +104 -0
- package/dist/depositKeys.test.d.ts +1 -0
- package/dist/depositKeys.test.js +231 -0
- package/dist/events.d.ts +41 -0
- package/dist/events.js +92 -0
- package/dist/events.test.d.ts +1 -0
- package/dist/events.test.js +111 -0
- package/dist/fees.d.ts +136 -0
- package/dist/fees.js +178 -0
- package/dist/fees.test.d.ts +1 -0
- package/dist/fees.test.js +165 -0
- package/dist/ids.d.ts +175 -0
- package/dist/ids.js +231 -0
- package/dist/ids.test.d.ts +1 -0
- package/dist/ids.test.js +607 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +45 -0
- package/dist/input.d.ts +26 -0
- package/dist/{src/input.js → input.js} +23 -4
- package/dist/intent.d.ts +50 -0
- package/dist/intent.js +28 -0
- package/dist/intent.test.d.ts +1 -0
- package/dist/intent.test.js +119 -0
- package/dist/json.d.ts +55 -0
- package/dist/json.js +80 -0
- package/dist/participants.d.ts +22 -0
- package/dist/participants.js +28 -0
- package/dist/payment.d.ts +481 -0
- package/dist/payment.js +1149 -0
- package/dist/payment.test.d.ts +1 -0
- package/dist/payment.test.js +272 -0
- package/dist/peer-types.d.ts +327 -0
- package/dist/peer-types.js +169 -0
- package/dist/profit.d.ts +49 -0
- package/dist/profit.js +57 -0
- package/dist/profit.test.d.ts +1 -0
- package/dist/profit.test.js +315 -0
- package/dist/reputation.d.ts +125 -0
- package/dist/reputation.js +188 -0
- package/dist/reputation.test.d.ts +1 -0
- package/dist/reputation.test.js +156 -0
- package/dist/sort.d.ts +75 -0
- package/dist/sort.js +70 -0
- package/dist/sort.test.d.ts +1 -0
- package/dist/sort.test.js +216 -0
- package/dist/stateKey.d.ts +35 -0
- package/dist/stateKey.js +36 -0
- package/dist/stats.d.ts +61 -0
- package/dist/stats.js +53 -0
- package/dist/stats.test.d.ts +1 -0
- package/dist/stats.test.js +104 -0
- package/dist/time.d.ts +104 -0
- package/dist/time.js +162 -0
- package/dist/time.test.d.ts +1 -0
- package/dist/time.test.js +231 -0
- package/dist/tokens.d.ts +15502 -0
- package/dist/{src/tokens.js → tokens.js} +98 -17
- package/dist/tokens.test.d.ts +1 -0
- package/dist/tokens.test.js +193 -0
- package/dist/units.d.ts +200 -0
- package/dist/units.js +279 -0
- package/dist/units.test.d.ts +1 -0
- package/dist/units.test.js +327 -0
- package/package.json +119 -29
- package/dist/scripts/convert-svg.js +0 -17
- package/dist/src/chain.d.ts +0 -14
- package/dist/src/chain.js +0 -18
- package/dist/src/contracts.d.ts +0 -30
- package/dist/src/contracts.js +0 -64
- package/dist/src/conversionRates.d.ts +0 -124
- package/dist/src/currencies.d.ts +0 -144
- package/dist/src/data/countries.json +0 -1099
- package/dist/src/deposit.d.ts +0 -1
- package/dist/src/events.d.ts +0 -3
- package/dist/src/events.js +0 -16
- package/dist/src/ids.d.ts +0 -72
- package/dist/src/ids.js +0 -81
- package/dist/src/index.d.ts +0 -16
- package/dist/src/index.js +0 -16
- package/dist/src/input.d.ts +0 -7
- package/dist/src/intent.d.ts +0 -14
- package/dist/src/json.d.ts +0 -18
- package/dist/src/json.js +0 -44
- package/dist/src/participants.d.ts +0 -7
- package/dist/src/participants.js +0 -13
- package/dist/src/payment.d.ts +0 -164
- package/dist/src/payment.js +0 -439
- package/dist/src/profit.d.ts +0 -28
- package/dist/src/profit.js +0 -34
- package/dist/src/sort.d.ts +0 -36
- package/dist/src/sort.js +0 -39
- package/dist/src/stats.d.ts +0 -7
- package/dist/src/stats.js +0 -8
- package/dist/src/time.d.ts +0 -54
- package/dist/src/time.js +0 -106
- package/dist/src/tokens.d.ts +0 -3675
- package/dist/src/units.d.ts +0 -17
- package/dist/src/units.js +0 -79
- package/dist/src/zkp2p-types.d.ts +0 -48
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/scripts/convert-svg.ts +0 -22
- package/src/chain.ts +0 -27
- package/src/contracts.ts +0 -89
- package/src/conversionRates.ts +0 -356
- package/src/currencies.ts +0 -286
- package/src/data/countries.json +0 -1099
- package/src/deposit.ts +0 -2
- package/src/events.ts +0 -21
- package/src/flags/eur.svg +0 -4
- package/src/flags/usd.svg +0 -25
- package/src/flags.ts +0 -4
- package/src/global.d.ts +0 -9
- package/src/ids.ts +0 -137
- package/src/index.ts +0 -16
- package/src/input.ts +0 -35
- package/src/intent.ts +0 -15
- package/src/json.ts +0 -42
- package/src/participants.ts +0 -14
- package/src/payment.ts +0 -573
- package/src/profit.ts +0 -55
- package/src/sort.ts +0 -49
- package/src/stats.ts +0 -10
- package/src/time.ts +0 -123
- package/src/tokens.ts +0 -159
- package/src/units.ts +0 -120
- package/src/zkp2p-types.ts +0 -55
- package/tsconfig.json +0 -18
- /package/dist/{scripts/convert-svg.d.ts → chain.test.d.ts} +0 -0
- /package/dist/{src/deposit.js → contracts.test.d.ts} +0 -0
- /package/dist/{src/intent.js → conversionRates.test.d.ts} +0 -0
- /package/dist/{src/zkp2p-types.js → currencies.test.d.ts} +0 -0
- /package/dist/{src/flags.d.ts → flags.d.ts} +0 -0
- /package/dist/{src/flags.js → flags.js} +0 -0
package/dist/time.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Time and date utilities for formatting and calculations.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Interval constants in milliseconds for time calculations
|
|
6
|
+
* - Human-readable relative time formatting ("5m ago", "2d 3h ago")
|
|
7
|
+
* - Duration formatting for time ranges
|
|
8
|
+
* - Absolute timestamp formatting
|
|
9
|
+
*
|
|
10
|
+
* @see {@link units} for blockchain timestamp conversion
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Time unit relationships for manual calculations.
|
|
14
|
+
* Use these when building complex time expressions.
|
|
15
|
+
*/
|
|
16
|
+
export const intervalIn = {
|
|
17
|
+
yearInDays: 365,
|
|
18
|
+
quarterInMonths: 3,
|
|
19
|
+
monthInDays: 30,
|
|
20
|
+
hourInDays: 24,
|
|
21
|
+
daysInWeek: 7,
|
|
22
|
+
minuteInHours: 60,
|
|
23
|
+
secondInMinutes: 60,
|
|
24
|
+
millisecondInSecond: 1000,
|
|
25
|
+
};
|
|
26
|
+
const second = intervalIn.millisecondInSecond;
|
|
27
|
+
const minute = intervalIn.secondInMinutes * second;
|
|
28
|
+
const hour = intervalIn.minuteInHours * minute;
|
|
29
|
+
const day = intervalIn.hourInDays * hour;
|
|
30
|
+
const month = intervalIn.monthInDays * day;
|
|
31
|
+
const year = intervalIn.yearInDays * month;
|
|
32
|
+
/**
|
|
33
|
+
* Common time intervals in milliseconds.
|
|
34
|
+
* Use for timeout calculations, polling intervals, and date math.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* // Set a 5 minute timeout
|
|
39
|
+
* setTimeout(callback, 5 * interval.minute)
|
|
40
|
+
*
|
|
41
|
+
* // Calculate time 1 day ago
|
|
42
|
+
* const yesterday = Date.now() - interval.day
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export const interval = {
|
|
46
|
+
second,
|
|
47
|
+
minute,
|
|
48
|
+
hour,
|
|
49
|
+
day,
|
|
50
|
+
month,
|
|
51
|
+
year,
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Default number of time units to display in statistics/charts.
|
|
55
|
+
* Used for "show last N hours/days/months" UI controls.
|
|
56
|
+
*/
|
|
57
|
+
export const defaultDisplayInterval = {
|
|
58
|
+
hour: 7 * 24,
|
|
59
|
+
day: 6 * 30,
|
|
60
|
+
month: 60,
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Format a timestamp as a human-readable relative time string.
|
|
64
|
+
* Produces compact strings like "5m ago", "2h 30m ago", "3d ago".
|
|
65
|
+
*
|
|
66
|
+
* @param timestamp - The timestamp to format (Date, number in ms, or date string)
|
|
67
|
+
* @param options - Optional configuration
|
|
68
|
+
* @param options.anchor - Reference date for "ago" calculation (default: now)
|
|
69
|
+
* @param options.showSeconds - Whether to show seconds for very recent times
|
|
70
|
+
* @returns Human-readable relative time string
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* formatTimestamp(Date.now() - 5 * 60 * 1000) // "5m ago"
|
|
75
|
+
* formatTimestamp(Date.now() - 2.5 * 60 * 60 * 1000) // "2h 30m ago"
|
|
76
|
+
* formatTimestamp(Date.now() - 3 * 24 * 60 * 60 * 1000) // "3d 0h ago"
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export const formatTimestamp = (timestamp, options) => {
|
|
80
|
+
const anchor = options?.anchor ?? new Date(), showSeconds = options?.showSeconds ?? false, date = new Date(timestamp);
|
|
81
|
+
const diffMs = anchor.getTime() - date.getTime(), diffMins = Math.floor(diffMs / interval.minute), diffHours = Math.floor(diffMs / interval.hour);
|
|
82
|
+
const diffDays = Math.floor(diffMs / interval.day), diffMonths = Math.floor(diffDays / intervalIn.monthInDays);
|
|
83
|
+
if (diffMonths > 0) {
|
|
84
|
+
if (diffMonths < intervalIn.quarterInMonths) {
|
|
85
|
+
return `${diffMonths}mo ${diffDays % intervalIn.monthInDays}d ago`;
|
|
86
|
+
}
|
|
87
|
+
return `${diffMonths}mo ago`;
|
|
88
|
+
}
|
|
89
|
+
if (diffMins < 1) {
|
|
90
|
+
if (showSeconds) {
|
|
91
|
+
if (diffMs < 10 * interval.second) {
|
|
92
|
+
return 'Just now';
|
|
93
|
+
}
|
|
94
|
+
return `${Math.floor(diffMs / interval.second)}s ago`;
|
|
95
|
+
}
|
|
96
|
+
return 'Just now';
|
|
97
|
+
}
|
|
98
|
+
if (diffMins < intervalIn.secondInMinutes) {
|
|
99
|
+
return `${diffMins}m ago`;
|
|
100
|
+
}
|
|
101
|
+
else if (diffHours < intervalIn.hourInDays) {
|
|
102
|
+
if (diffHours < 6) {
|
|
103
|
+
return `${diffHours}h ${diffMins % intervalIn.minuteInHours}m ago`;
|
|
104
|
+
}
|
|
105
|
+
return `${diffHours}h ago`;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
if (diffDays < intervalIn.daysInWeek) {
|
|
109
|
+
return `${diffDays}d ${diffHours % intervalIn.hourInDays}h ago`;
|
|
110
|
+
}
|
|
111
|
+
return `${diffDays}d ago`;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Format a duration
|
|
116
|
+
* @param param0.start - The start date
|
|
117
|
+
* @param param0.until - The end date
|
|
118
|
+
* @returns The formatted duration
|
|
119
|
+
*/
|
|
120
|
+
export const formatDuration = ({ start = new Date(), until = new Date(), } = {}) => {
|
|
121
|
+
const diffMs = until.getTime() - start.getTime();
|
|
122
|
+
const diffMins = Math.floor(diffMs / interval.minute);
|
|
123
|
+
const diffHours = Math.floor(diffMs / interval.hour);
|
|
124
|
+
const diffDays = Math.floor(diffMs / interval.day);
|
|
125
|
+
const diffMonths = Math.floor(diffDays / intervalIn.monthInDays);
|
|
126
|
+
if (diffMonths > 0) {
|
|
127
|
+
return `${diffMonths}mo ${diffDays % intervalIn.monthInDays}d`;
|
|
128
|
+
}
|
|
129
|
+
if (diffDays > 0) {
|
|
130
|
+
return `${diffDays}d ${diffHours % intervalIn.hourInDays}h`;
|
|
131
|
+
}
|
|
132
|
+
if (diffHours > 0) {
|
|
133
|
+
return `${diffHours}h ${diffMins % intervalIn.minuteInHours}m`;
|
|
134
|
+
}
|
|
135
|
+
return `${diffMins % intervalIn.minuteInHours}m`;
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Format an absolute timestamp to a human-readable string
|
|
139
|
+
* @param timestamp - Unix timestamp in seconds or milliseconds (auto-detected)
|
|
140
|
+
* @param options - Intl.DateTimeFormatOptions for formatting
|
|
141
|
+
* @returns Formatted absolute timestamp string
|
|
142
|
+
*/
|
|
143
|
+
export const formatAbsoluteTimestamp = (timestamp, options) => {
|
|
144
|
+
// Auto-detect if timestamp is in seconds or milliseconds
|
|
145
|
+
// Timestamps > 10 billion are assumed to be in milliseconds (year 2286+ if seconds)
|
|
146
|
+
const timestampMs = Number(timestamp) > 10000000000 ? Number(timestamp) : Number(timestamp) * 1000;
|
|
147
|
+
const date = new Date(timestampMs);
|
|
148
|
+
return date.toLocaleString(undefined, options ?? {
|
|
149
|
+
year: 'numeric',
|
|
150
|
+
month: 'short',
|
|
151
|
+
day: 'numeric',
|
|
152
|
+
hour: '2-digit',
|
|
153
|
+
minute: '2-digit',
|
|
154
|
+
second: '2-digit'
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Sleep for a given number of milliseconds
|
|
159
|
+
* @param ms - The number of milliseconds to sleep
|
|
160
|
+
* @returns A promise that resolves after the given number of milliseconds
|
|
161
|
+
*/
|
|
162
|
+
export const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { interval, intervalIn, formatTimestamp, formatDuration, formatAbsoluteTimestamp, sleep, defaultDisplayInterval, } from './time.js';
|
|
4
|
+
describe('time', () => {
|
|
5
|
+
describe('formatTimestamp', () => {
|
|
6
|
+
const anchor = new Date('2025-01-05T12:00:00Z');
|
|
7
|
+
it('should return "Just now" for very recent timestamps', () => {
|
|
8
|
+
const recent = new Date(anchor.getTime() - 5 * 1000); // 5 seconds ago
|
|
9
|
+
assert.equal(formatTimestamp(recent, { anchor }), 'Just now');
|
|
10
|
+
});
|
|
11
|
+
it('should show seconds when enabled', () => {
|
|
12
|
+
const recent = new Date(anchor.getTime() - 30 * 1000); // 30 seconds ago
|
|
13
|
+
assert.equal(formatTimestamp(recent, { anchor, showSeconds: true }), '30s ago');
|
|
14
|
+
});
|
|
15
|
+
it('should format minutes correctly', () => {
|
|
16
|
+
const mins = new Date(anchor.getTime() - 15 * 60 * 1000); // 15 minutes ago
|
|
17
|
+
assert.equal(formatTimestamp(mins, { anchor }), '15m ago');
|
|
18
|
+
});
|
|
19
|
+
it('should format hours with minutes for recent hours', () => {
|
|
20
|
+
const hours = new Date(anchor.getTime() - 3 * 60 * 60 * 1000 - 30 * 60 * 1000); // 3h 30m ago
|
|
21
|
+
assert.equal(formatTimestamp(hours, { anchor }), '3h 30m ago');
|
|
22
|
+
});
|
|
23
|
+
it('should format hours only for older hours', () => {
|
|
24
|
+
const hours = new Date(anchor.getTime() - 8 * 60 * 60 * 1000); // 8 hours ago
|
|
25
|
+
assert.equal(formatTimestamp(hours, { anchor }), '8h ago');
|
|
26
|
+
});
|
|
27
|
+
it('should format days with hours for recent days', () => {
|
|
28
|
+
const days = new Date(anchor.getTime() - 2 * 24 * 60 * 60 * 1000 - 5 * 60 * 60 * 1000); // 2d 5h ago
|
|
29
|
+
assert.equal(formatTimestamp(days, { anchor }), '2d 5h ago');
|
|
30
|
+
});
|
|
31
|
+
it('should format days only for older days', () => {
|
|
32
|
+
const days = new Date(anchor.getTime() - 10 * 24 * 60 * 60 * 1000); // 10 days ago
|
|
33
|
+
assert.equal(formatTimestamp(days, { anchor }), '10d ago');
|
|
34
|
+
});
|
|
35
|
+
it('should format months with days for recent months', () => {
|
|
36
|
+
const twoMonthsAgo = new Date(anchor.getTime() - 60 * 24 * 60 * 60 * 1000); // 60 days = 2 months
|
|
37
|
+
const result = formatTimestamp(twoMonthsAgo, { anchor });
|
|
38
|
+
// 2 months is less than a quarter, so shows "2mo 0d ago"
|
|
39
|
+
assert.equal(result, '2mo 0d ago');
|
|
40
|
+
});
|
|
41
|
+
it('should format months only for older months', () => {
|
|
42
|
+
const fourMonthsAgo = new Date(anchor.getTime() - 120 * 24 * 60 * 60 * 1000); // 120 days = 4 months
|
|
43
|
+
const result = formatTimestamp(fourMonthsAgo, { anchor });
|
|
44
|
+
// 4 months is >= a quarter (3 months), so shows just "4mo ago"
|
|
45
|
+
assert.equal(result, '4mo ago');
|
|
46
|
+
});
|
|
47
|
+
it('should handle Date objects', () => {
|
|
48
|
+
const date = new Date(anchor.getTime() - 5 * 60 * 1000);
|
|
49
|
+
assert.equal(formatTimestamp(date, { anchor }), '5m ago');
|
|
50
|
+
});
|
|
51
|
+
it('should handle timestamp numbers', () => {
|
|
52
|
+
const timestamp = anchor.getTime() - 5 * 60 * 1000;
|
|
53
|
+
assert.equal(formatTimestamp(timestamp, { anchor }), '5m ago');
|
|
54
|
+
});
|
|
55
|
+
it('should handle ISO strings', () => {
|
|
56
|
+
const isoString = new Date(anchor.getTime() - 5 * 60 * 1000).toISOString();
|
|
57
|
+
assert.equal(formatTimestamp(isoString, { anchor }), '5m ago');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('formatDuration', () => {
|
|
61
|
+
it('should format minute durations', () => {
|
|
62
|
+
const start = new Date('2025-01-05T12:00:00Z');
|
|
63
|
+
const until = new Date('2025-01-05T12:15:00Z');
|
|
64
|
+
assert.equal(formatDuration({ start, until }), '15m');
|
|
65
|
+
});
|
|
66
|
+
it('should format hour durations with minutes', () => {
|
|
67
|
+
const start = new Date('2025-01-05T12:00:00Z');
|
|
68
|
+
const until = new Date('2025-01-05T15:30:00Z');
|
|
69
|
+
assert.equal(formatDuration({ start, until }), '3h 30m');
|
|
70
|
+
});
|
|
71
|
+
it('should format day durations with hours', () => {
|
|
72
|
+
const start = new Date('2025-01-05T12:00:00Z');
|
|
73
|
+
const until = new Date('2025-01-07T18:00:00Z');
|
|
74
|
+
assert.equal(formatDuration({ start, until }), '2d 6h');
|
|
75
|
+
});
|
|
76
|
+
it('should format month durations with days', () => {
|
|
77
|
+
const start = new Date('2025-01-05T12:00:00Z');
|
|
78
|
+
const until = new Date('2025-02-20T12:00:00Z');
|
|
79
|
+
const result = formatDuration({ start, until });
|
|
80
|
+
assert.ok(result.includes('mo'));
|
|
81
|
+
assert.ok(result.includes('d'));
|
|
82
|
+
});
|
|
83
|
+
it('should handle zero duration', () => {
|
|
84
|
+
const date = new Date('2025-01-05T12:00:00Z');
|
|
85
|
+
assert.equal(formatDuration({ start: date, until: date }), '0m');
|
|
86
|
+
});
|
|
87
|
+
it('should default to current time if no dates provided', () => {
|
|
88
|
+
const result = formatDuration();
|
|
89
|
+
assert.equal(result, '0m');
|
|
90
|
+
});
|
|
91
|
+
it('should handle only start date', () => {
|
|
92
|
+
const start = new Date(Date.now() - 5 * 60 * 1000);
|
|
93
|
+
const result = formatDuration({ start });
|
|
94
|
+
assert.ok(result.includes('m'));
|
|
95
|
+
});
|
|
96
|
+
it('should handle only until date', () => {
|
|
97
|
+
const until = new Date(Date.now() + 5 * 60 * 1000);
|
|
98
|
+
const result = formatDuration({ until });
|
|
99
|
+
assert.ok(result.includes('m'));
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('sleep', () => {
|
|
103
|
+
it('should resolve after specified milliseconds', async () => {
|
|
104
|
+
const start = Date.now();
|
|
105
|
+
await sleep(100);
|
|
106
|
+
const elapsed = Date.now() - start;
|
|
107
|
+
assert.ok(elapsed >= 100, `Expected at least 100ms, got ${elapsed}ms`);
|
|
108
|
+
// Allow generous upper bound for CI environments with high load
|
|
109
|
+
assert.ok(elapsed < 1000, `Expected less than 1000ms, got ${elapsed}ms`);
|
|
110
|
+
});
|
|
111
|
+
it('should return a promise', () => {
|
|
112
|
+
const result = sleep(1);
|
|
113
|
+
assert.ok(result instanceof Promise);
|
|
114
|
+
});
|
|
115
|
+
it('should resolve immediately for 0ms', async () => {
|
|
116
|
+
const start = Date.now();
|
|
117
|
+
await sleep(0);
|
|
118
|
+
const elapsed = Date.now() - start;
|
|
119
|
+
assert.ok(elapsed < 10, `Expected less than 10ms, got ${elapsed}ms`);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe('formatAbsoluteTimestamp', () => {
|
|
123
|
+
it('should format timestamp in seconds', () => {
|
|
124
|
+
// Unix timestamp for 2024-01-01 00:00:00 UTC
|
|
125
|
+
const timestamp = 1704067200;
|
|
126
|
+
const result = formatAbsoluteTimestamp(timestamp);
|
|
127
|
+
// Result format varies by timezone, just check it's a valid date string
|
|
128
|
+
assert.ok(result.length > 0);
|
|
129
|
+
assert.ok(typeof result === 'string');
|
|
130
|
+
});
|
|
131
|
+
it('should format timestamp in milliseconds', () => {
|
|
132
|
+
// Unix timestamp in ms for 2024-01-01 00:00:00 UTC
|
|
133
|
+
const timestamp = 1704067200000;
|
|
134
|
+
const result = formatAbsoluteTimestamp(timestamp);
|
|
135
|
+
assert.ok(result.length > 0);
|
|
136
|
+
assert.ok(typeof result === 'string');
|
|
137
|
+
});
|
|
138
|
+
it('should handle bigint timestamps', () => {
|
|
139
|
+
const timestamp = 1704067200n;
|
|
140
|
+
const result = formatAbsoluteTimestamp(timestamp);
|
|
141
|
+
assert.ok(result.length > 0);
|
|
142
|
+
});
|
|
143
|
+
it('should accept custom format options', () => {
|
|
144
|
+
const timestamp = 1704067200;
|
|
145
|
+
const result = formatAbsoluteTimestamp(timestamp, {
|
|
146
|
+
year: 'numeric',
|
|
147
|
+
month: 'numeric',
|
|
148
|
+
day: 'numeric',
|
|
149
|
+
timeZone: 'UTC', // Use UTC to get consistent results
|
|
150
|
+
});
|
|
151
|
+
assert.ok(result.includes('2024') || result.includes('1/1')); // Different locales format differently
|
|
152
|
+
});
|
|
153
|
+
it('should auto-detect milliseconds for large numbers', () => {
|
|
154
|
+
// Timestamp > 10 billion should be treated as milliseconds
|
|
155
|
+
const timestampMs = 1704067200000;
|
|
156
|
+
const timestampSec = 1704067200;
|
|
157
|
+
const resultMs = formatAbsoluteTimestamp(timestampMs);
|
|
158
|
+
const resultSec = formatAbsoluteTimestamp(timestampSec);
|
|
159
|
+
// Both should produce the same date
|
|
160
|
+
assert.equal(resultMs, resultSec);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
describe('interval constants', () => {
|
|
164
|
+
it('should have correct second value', () => {
|
|
165
|
+
assert.equal(interval.second, 1000);
|
|
166
|
+
});
|
|
167
|
+
it('should have correct minute value', () => {
|
|
168
|
+
assert.equal(interval.minute, 60 * 1000);
|
|
169
|
+
});
|
|
170
|
+
it('should have correct hour value', () => {
|
|
171
|
+
assert.equal(interval.hour, 60 * 60 * 1000);
|
|
172
|
+
});
|
|
173
|
+
it('should have correct day value', () => {
|
|
174
|
+
assert.equal(interval.day, 24 * 60 * 60 * 1000);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe('intervalIn constants', () => {
|
|
178
|
+
it('should have correct values', () => {
|
|
179
|
+
assert.equal(intervalIn.yearInDays, 365);
|
|
180
|
+
assert.equal(intervalIn.quarterInMonths, 3);
|
|
181
|
+
assert.equal(intervalIn.monthInDays, 30);
|
|
182
|
+
assert.equal(intervalIn.hourInDays, 24);
|
|
183
|
+
assert.equal(intervalIn.daysInWeek, 7);
|
|
184
|
+
assert.equal(intervalIn.minuteInHours, 60);
|
|
185
|
+
assert.equal(intervalIn.secondInMinutes, 60);
|
|
186
|
+
assert.equal(intervalIn.millisecondInSecond, 1000);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
describe('defaultDisplayInterval', () => {
|
|
190
|
+
it('should have correct default values', () => {
|
|
191
|
+
assert.equal(defaultDisplayInterval.hour, 7 * 24);
|
|
192
|
+
assert.equal(defaultDisplayInterval.day, 6 * 30);
|
|
193
|
+
assert.equal(defaultDisplayInterval.month, 60);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
describe('formatTimestamp edge cases', () => {
|
|
197
|
+
const anchor = new Date('2025-01-05T12:00:00Z');
|
|
198
|
+
it('should show "Just now" for showSeconds with very recent timestamp', () => {
|
|
199
|
+
const recent = new Date(anchor.getTime() - 5 * 1000); // 5 seconds ago
|
|
200
|
+
assert.equal(formatTimestamp(recent, { anchor, showSeconds: true }), 'Just now');
|
|
201
|
+
});
|
|
202
|
+
it('should show seconds for 10-59 seconds ago', () => {
|
|
203
|
+
const recent = new Date(anchor.getTime() - 15 * 1000); // 15 seconds ago
|
|
204
|
+
const result = formatTimestamp(recent, { anchor, showSeconds: true });
|
|
205
|
+
assert.equal(result, '15s ago');
|
|
206
|
+
});
|
|
207
|
+
it('should use default anchor and showSeconds when no options provided', () => {
|
|
208
|
+
// Test calling with no options object to cover default parameter initialization
|
|
209
|
+
const recent = new Date(Date.now() - 5 * 60 * 1000); // 5 minutes ago
|
|
210
|
+
const result = formatTimestamp(recent);
|
|
211
|
+
assert.ok(result.includes('m ago'));
|
|
212
|
+
});
|
|
213
|
+
it('should use defaults when empty options object provided', () => {
|
|
214
|
+
// Test calling with empty options to cover default parameter values
|
|
215
|
+
const recent = new Date(Date.now() - 10 * 60 * 1000); // 10 minutes ago
|
|
216
|
+
const result = formatTimestamp(recent, {});
|
|
217
|
+
assert.ok(result.includes('m ago'));
|
|
218
|
+
});
|
|
219
|
+
it('should use default anchor when only showSeconds provided', () => {
|
|
220
|
+
const recent = new Date(Date.now() - 30 * 1000); // 30 seconds ago
|
|
221
|
+
const result = formatTimestamp(recent, { showSeconds: true });
|
|
222
|
+
assert.ok(result.includes('s ago'));
|
|
223
|
+
});
|
|
224
|
+
it('should use default showSeconds when only anchor provided', () => {
|
|
225
|
+
const anchor = new Date('2025-01-05T12:00:00Z');
|
|
226
|
+
const recent = new Date(anchor.getTime() - 5 * 1000); // 5 seconds ago
|
|
227
|
+
const result = formatTimestamp(recent, { anchor });
|
|
228
|
+
assert.equal(result, 'Just now'); // showSeconds defaults to false
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|