@enormora/wall-clock 0.0.1
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/LICENSE +21 -0
- package/README.md +243 -0
- package/deterministic-wall-clock.d.ts +10 -0
- package/deterministic-wall-clock.d.ts.map +1 -0
- package/deterministic-wall-clock.js +115 -0
- package/deterministic-wall-clock.js.map +1 -0
- package/index.d.ts +5 -0
- package/index.d.ts.map +1 -0
- package/index.js +3 -0
- package/index.js.map +1 -0
- package/package.json +26 -0
- package/sbom.cdx.json +30 -0
- package/timer-delay.js +18 -0
- package/timer-delay.js.map +1 -0
- package/wall-clock.d.ts +10 -0
- package/wall-clock.d.ts.map +1 -0
- package/wall-clock.js +15 -0
- package/wall-clock.js.map +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Christian Rackerseder
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# @enormora/wall-clock
|
|
2
|
+
|
|
3
|
+
Explicit wall-clock time access for TypeScript applications.
|
|
4
|
+
|
|
5
|
+
`@enormora/wall-clock` provides a small boundary around time-related side effects:
|
|
6
|
+
|
|
7
|
+
- reading the current timestamp
|
|
8
|
+
- creating the current `Date`
|
|
9
|
+
- scheduling and clearing timeouts
|
|
10
|
+
- scheduling and clearing intervals
|
|
11
|
+
- replacing real time with a deterministic clock in tests
|
|
12
|
+
|
|
13
|
+
The package is intentionally small. It is meant to make time an explicit dependency instead of letting application code
|
|
14
|
+
call `Date.now()`, `new Date()`, `setTimeout`, or `setInterval` directly.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install @enormora/wall-clock
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The package is ESM-only and requires Node.js `^24.15.0 || ^26.0.0`.
|
|
23
|
+
|
|
24
|
+
The root export keeps both clocks available from one import. Prefer the explicit module subpaths when code only needs one
|
|
25
|
+
clock:
|
|
26
|
+
|
|
27
|
+
- `@enormora/wall-clock/wall-clock`
|
|
28
|
+
- `@enormora/wall-clock/deterministic-wall-clock`
|
|
29
|
+
|
|
30
|
+
## Real wall clock
|
|
31
|
+
|
|
32
|
+
Use `createWallClock()` at the application boundary and pass the resulting `WallClock` into code that needs time.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { createWallClock } from '@enormora/wall-clock/wall-clock';
|
|
36
|
+
|
|
37
|
+
const wallClock = createWallClock();
|
|
38
|
+
|
|
39
|
+
console.log(wallClock.currentTimestampInMilliseconds);
|
|
40
|
+
console.log(wallClock.currentDate.toISOString());
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The real clock delegates to the runtime:
|
|
44
|
+
|
|
45
|
+
- `currentTimestampInMilliseconds` uses `Date.now()`
|
|
46
|
+
- `currentDate` returns a new `Date`
|
|
47
|
+
- `setTimeout` and `clearTimeout` call `globalThis`
|
|
48
|
+
- `setInterval` and `clearInterval` call `globalThis`
|
|
49
|
+
|
|
50
|
+
The timer functions are bound to `globalThis`, so they can be passed around safely.
|
|
51
|
+
|
|
52
|
+
## Dependency injection
|
|
53
|
+
|
|
54
|
+
Prefer accepting a `WallClock` as an explicit dependency for code that depends on time.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import type { WallClock } from '@enormora/wall-clock/wall-clock';
|
|
58
|
+
|
|
59
|
+
type Session = {
|
|
60
|
+
readonly expiresAtTimestampInMilliseconds: number;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export function isSessionExpired(wallClock: WallClock, session: Session): boolean {
|
|
64
|
+
return wallClock.currentTimestampInMilliseconds >= session.expiresAtTimestampInMilliseconds;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This keeps the core behavior deterministic and easy to test.
|
|
69
|
+
|
|
70
|
+
## Deterministic wall clock
|
|
71
|
+
|
|
72
|
+
Use `createDeterministicWallClock()` in tests or deterministic environments.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { createDeterministicWallClock } from '@enormora/wall-clock/deterministic-wall-clock';
|
|
76
|
+
|
|
77
|
+
const wallClock = createDeterministicWallClock({
|
|
78
|
+
initialCurrentTimestampInMilliseconds: 1_704_067_200_000
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
console.log(wallClock.currentDate.toISOString());
|
|
82
|
+
|
|
83
|
+
wallClock.advanceByMilliseconds(1000);
|
|
84
|
+
|
|
85
|
+
console.log(wallClock.currentTimestampInMilliseconds);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The deterministic clock implements the same `WallClock` interface and adds:
|
|
89
|
+
|
|
90
|
+
- `setCurrentTimestampInMilliseconds(nextTimestampInMilliseconds)`
|
|
91
|
+
- `advanceByMilliseconds(delayInMilliseconds)`
|
|
92
|
+
|
|
93
|
+
## Testing timers
|
|
94
|
+
|
|
95
|
+
The deterministic clock runs scheduled callbacks when time is advanced far enough.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { deepEqual } from 'node:assert/strict';
|
|
99
|
+
import { createDeterministicWallClock } from '@enormora/wall-clock/deterministic-wall-clock';
|
|
100
|
+
|
|
101
|
+
const wallClock = createDeterministicWallClock();
|
|
102
|
+
const calls: string[] = [];
|
|
103
|
+
|
|
104
|
+
wallClock.setTimeout(
|
|
105
|
+
(value) => {
|
|
106
|
+
calls.push(value);
|
|
107
|
+
},
|
|
108
|
+
100,
|
|
109
|
+
'done'
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
wallClock.advanceByMilliseconds(99);
|
|
113
|
+
deepEqual(calls, []);
|
|
114
|
+
|
|
115
|
+
wallClock.advanceByMilliseconds(1);
|
|
116
|
+
deepEqual(calls, ['done']);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Intervals run once for each elapsed interval.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { equal } from 'node:assert/strict';
|
|
123
|
+
import { createDeterministicWallClock } from '@enormora/wall-clock/deterministic-wall-clock';
|
|
124
|
+
|
|
125
|
+
const wallClock = createDeterministicWallClock();
|
|
126
|
+
let count = 0;
|
|
127
|
+
|
|
128
|
+
const intervalIdentifier = wallClock.setInterval(() => {
|
|
129
|
+
count += 1;
|
|
130
|
+
}, 100);
|
|
131
|
+
|
|
132
|
+
wallClock.advanceByMilliseconds(250);
|
|
133
|
+
equal(count, 2);
|
|
134
|
+
|
|
135
|
+
wallClock.clearInterval(intervalIdentifier);
|
|
136
|
+
wallClock.advanceByMilliseconds(500);
|
|
137
|
+
equal(count, 2);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Timer behavior
|
|
141
|
+
|
|
142
|
+
Timeouts:
|
|
143
|
+
|
|
144
|
+
- execute once
|
|
145
|
+
- execute only after the clock reaches their scheduled timestamp
|
|
146
|
+
- execute in scheduled timestamp order
|
|
147
|
+
- execute in registration order when multiple timeouts share the same scheduled timestamp
|
|
148
|
+
- can use a delay of `0`
|
|
149
|
+
- reject negative and non-finite delays
|
|
150
|
+
|
|
151
|
+
Intervals:
|
|
152
|
+
|
|
153
|
+
- execute repeatedly
|
|
154
|
+
- execute once per elapsed interval when time advances
|
|
155
|
+
- stop after `clearInterval`
|
|
156
|
+
- reject `0`, negative, and non-finite delays
|
|
157
|
+
|
|
158
|
+
The deterministic clock intentionally rejects zero-delay intervals because they cannot advance safely without creating an
|
|
159
|
+
infinite loop.
|
|
160
|
+
|
|
161
|
+
## API
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
export type WallClock = {
|
|
165
|
+
readonly currentTimestampInMilliseconds: number;
|
|
166
|
+
readonly currentDate: Date;
|
|
167
|
+
readonly setTimeout: <HandlerArguments extends readonly unknown[]>(
|
|
168
|
+
handler: (...handlerArguments: HandlerArguments) => void,
|
|
169
|
+
delayInMilliseconds: number,
|
|
170
|
+
...handlerArguments: HandlerArguments
|
|
171
|
+
) => ReturnType<typeof globalThis.setTimeout>;
|
|
172
|
+
readonly clearTimeout: (timeoutIdentifier: ReturnType<typeof globalThis.setTimeout>) => void;
|
|
173
|
+
readonly setInterval: <HandlerArguments extends readonly unknown[]>(
|
|
174
|
+
handler: (...handlerArguments: HandlerArguments) => void,
|
|
175
|
+
delayInMilliseconds: number,
|
|
176
|
+
...handlerArguments: HandlerArguments
|
|
177
|
+
) => ReturnType<typeof globalThis.setInterval>;
|
|
178
|
+
readonly clearInterval: (intervalIdentifier: ReturnType<typeof globalThis.setInterval>) => void;
|
|
179
|
+
};
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
export type DeterministicWallClock = WallClock & {
|
|
184
|
+
readonly setCurrentTimestampInMilliseconds: (nextTimestampInMilliseconds: number) => void;
|
|
185
|
+
readonly advanceByMilliseconds: (delayInMilliseconds: number) => void;
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
export function createWallClock(): WallClock;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
export function createDeterministicWallClock(options?: {
|
|
195
|
+
readonly initialCurrentTimestampInMilliseconds?: number;
|
|
196
|
+
}): DeterministicWallClock;
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
Install dependencies:
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
npm clean-install
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Compile:
|
|
208
|
+
|
|
209
|
+
```sh
|
|
210
|
+
just compile
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Run lint checks:
|
|
214
|
+
|
|
215
|
+
```sh
|
|
216
|
+
just lint
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Run tests:
|
|
220
|
+
|
|
221
|
+
```sh
|
|
222
|
+
just test
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Run a Packtory dry-run:
|
|
226
|
+
|
|
227
|
+
```sh
|
|
228
|
+
just packtory-dry-run
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Publishing
|
|
232
|
+
|
|
233
|
+
The package is prepared with Packtory. The dry-run command validates the package shape without publishing.
|
|
234
|
+
|
|
235
|
+
```sh
|
|
236
|
+
just packtory-dry-run
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Actual publishing should use the repository release workflow once configured.
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { WallClock } from './wall-clock.ts';
|
|
2
|
+
export type DeterministicWallClock = WallClock & {
|
|
3
|
+
readonly setCurrentTimestampInMilliseconds: (nextTimestampInMilliseconds: number) => void;
|
|
4
|
+
readonly advanceByMilliseconds: (delayInMilliseconds: number) => void;
|
|
5
|
+
};
|
|
6
|
+
export type DeterministicWallClockOptions = {
|
|
7
|
+
readonly initialCurrentTimestampInMilliseconds?: number;
|
|
8
|
+
};
|
|
9
|
+
export declare function createDeterministicWallClock(options?: DeterministicWallClockOptions): DeterministicWallClock;
|
|
10
|
+
//# sourceMappingURL=deterministic-wall-clock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deterministic-wall-clock.d.ts","sourceRoot":"","sources":["../../../source/deterministic-wall-clock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG;IAC7C,QAAQ,CAAC,iCAAiC,EAAE,CAAC,2BAA2B,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1F,QAAQ,CAAC,qBAAqB,EAAE,CAAC,mBAAmB,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IACxC,QAAQ,CAAC,qCAAqC,CAAC,EAAE,MAAM,CAAC;CAC3D,CAAC;AAwIF,wBAAgB,4BAA4B,CAAC,OAAO,GAAE,6BAAkC,GAAG,sBAAsB,CAuChH"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { validateIntervalDelayInMilliseconds, validateTimeoutDelayInMilliseconds } from "./timer-delay.js";
|
|
2
|
+
function createTimeoutController(currentTimestampInMilliseconds) {
|
|
3
|
+
let nextTimeoutIdentifier = 0;
|
|
4
|
+
const timeoutRegistrations = new Map();
|
|
5
|
+
function runDueTimeoutRegistrations() {
|
|
6
|
+
const dueTimeoutEntries = Array.from(timeoutRegistrations.entries())
|
|
7
|
+
.filter(([, timeoutRegistration]) => {
|
|
8
|
+
return timeoutRegistration.executionTimestampInMilliseconds <= currentTimestampInMilliseconds();
|
|
9
|
+
})
|
|
10
|
+
.toSorted((firstTimeoutEntry, secondTimeoutEntry) => {
|
|
11
|
+
const [firstTimeoutIdentifier, firstTimeoutRegistration] = firstTimeoutEntry;
|
|
12
|
+
const [secondTimeoutIdentifier, secondTimeoutRegistration] = secondTimeoutEntry;
|
|
13
|
+
if (firstTimeoutRegistration.executionTimestampInMilliseconds !==
|
|
14
|
+
secondTimeoutRegistration.executionTimestampInMilliseconds) {
|
|
15
|
+
return (firstTimeoutRegistration.executionTimestampInMilliseconds -
|
|
16
|
+
secondTimeoutRegistration.executionTimestampInMilliseconds);
|
|
17
|
+
}
|
|
18
|
+
return firstTimeoutIdentifier - secondTimeoutIdentifier;
|
|
19
|
+
});
|
|
20
|
+
dueTimeoutEntries.forEach(([timeoutIdentifier, timeoutRegistration]) => {
|
|
21
|
+
timeoutRegistrations.delete(timeoutIdentifier);
|
|
22
|
+
timeoutRegistration.execute();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
runDueTimeoutRegistrations,
|
|
27
|
+
setTimeout(handler, delayInMilliseconds, ...handlerArguments) {
|
|
28
|
+
validateTimeoutDelayInMilliseconds(delayInMilliseconds);
|
|
29
|
+
const timeoutIdentifier = nextTimeoutIdentifier;
|
|
30
|
+
nextTimeoutIdentifier += 1;
|
|
31
|
+
timeoutRegistrations.set(timeoutIdentifier, {
|
|
32
|
+
execute: () => {
|
|
33
|
+
handler(...handlerArguments);
|
|
34
|
+
},
|
|
35
|
+
executionTimestampInMilliseconds: currentTimestampInMilliseconds() + delayInMilliseconds
|
|
36
|
+
});
|
|
37
|
+
return timeoutIdentifier;
|
|
38
|
+
},
|
|
39
|
+
clearTimeout(timeoutIdentifier) {
|
|
40
|
+
timeoutRegistrations.delete(timeoutIdentifier);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function createIntervalController(currentTimestampInMilliseconds) {
|
|
45
|
+
let nextIntervalIdentifier = 0;
|
|
46
|
+
const intervalRegistrations = new Map();
|
|
47
|
+
function runDueIntervalRegistrations() {
|
|
48
|
+
intervalRegistrations.forEach((intervalRegistration, intervalIdentifier) => {
|
|
49
|
+
const { delayInMilliseconds, execute } = intervalRegistration;
|
|
50
|
+
let { nextExecutionTimestampInMilliseconds } = intervalRegistration;
|
|
51
|
+
while (nextExecutionTimestampInMilliseconds <= currentTimestampInMilliseconds()) {
|
|
52
|
+
execute();
|
|
53
|
+
if (!intervalRegistrations.has(intervalIdentifier)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
nextExecutionTimestampInMilliseconds += delayInMilliseconds;
|
|
57
|
+
intervalRegistrations.set(intervalIdentifier, {
|
|
58
|
+
delayInMilliseconds,
|
|
59
|
+
execute,
|
|
60
|
+
nextExecutionTimestampInMilliseconds
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
runDueIntervalRegistrations,
|
|
67
|
+
setInterval(handler, delayInMilliseconds, ...handlerArguments) {
|
|
68
|
+
validateIntervalDelayInMilliseconds(delayInMilliseconds);
|
|
69
|
+
const intervalIdentifier = nextIntervalIdentifier;
|
|
70
|
+
nextIntervalIdentifier += 1;
|
|
71
|
+
intervalRegistrations.set(intervalIdentifier, {
|
|
72
|
+
delayInMilliseconds,
|
|
73
|
+
execute: () => {
|
|
74
|
+
handler(...handlerArguments);
|
|
75
|
+
},
|
|
76
|
+
nextExecutionTimestampInMilliseconds: currentTimestampInMilliseconds() + delayInMilliseconds
|
|
77
|
+
});
|
|
78
|
+
return intervalIdentifier;
|
|
79
|
+
},
|
|
80
|
+
clearInterval(intervalIdentifier) {
|
|
81
|
+
intervalRegistrations.delete(intervalIdentifier);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export function createDeterministicWallClock(options = {}) {
|
|
86
|
+
const { initialCurrentTimestampInMilliseconds = 0 } = options;
|
|
87
|
+
let currentTimestampInMilliseconds = initialCurrentTimestampInMilliseconds;
|
|
88
|
+
function currentTimestampReader() {
|
|
89
|
+
return currentTimestampInMilliseconds;
|
|
90
|
+
}
|
|
91
|
+
const timeoutController = createTimeoutController(currentTimestampReader);
|
|
92
|
+
const intervalController = createIntervalController(currentTimestampReader);
|
|
93
|
+
return {
|
|
94
|
+
get currentTimestampInMilliseconds() {
|
|
95
|
+
return currentTimestampInMilliseconds;
|
|
96
|
+
},
|
|
97
|
+
get currentDate() {
|
|
98
|
+
return new Date(currentTimestampInMilliseconds);
|
|
99
|
+
},
|
|
100
|
+
setCurrentTimestampInMilliseconds(nextTimestampInMilliseconds) {
|
|
101
|
+
currentTimestampInMilliseconds = nextTimestampInMilliseconds;
|
|
102
|
+
},
|
|
103
|
+
advanceByMilliseconds(delayInMilliseconds) {
|
|
104
|
+
validateTimeoutDelayInMilliseconds(delayInMilliseconds);
|
|
105
|
+
currentTimestampInMilliseconds += delayInMilliseconds;
|
|
106
|
+
intervalController.runDueIntervalRegistrations();
|
|
107
|
+
timeoutController.runDueTimeoutRegistrations();
|
|
108
|
+
},
|
|
109
|
+
setTimeout: timeoutController.setTimeout,
|
|
110
|
+
clearTimeout: timeoutController.clearTimeout,
|
|
111
|
+
setInterval: intervalController.setInterval,
|
|
112
|
+
clearInterval: intervalController.clearInterval
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=deterministic-wall-clock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deterministic-wall-clock.js","sourceRoot":"","sources":["../../../source/deterministic-wall-clock.ts"],"names":[],"mappings":"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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createWallClock } from './wall-clock.ts';
|
|
2
|
+
export type { WallClock } from './wall-clock.ts';
|
|
3
|
+
export { createDeterministicWallClock } from './deterministic-wall-clock.ts';
|
|
4
|
+
export type { DeterministicWallClock, DeterministicWallClockOptions } from './deterministic-wall-clock.ts';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../source/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAC7E,YAAY,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC"}
|
package/index.js
ADDED
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../source/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "Christian Rackerseder <git@echooff.de>",
|
|
3
|
+
"exports": {
|
|
4
|
+
".": {
|
|
5
|
+
"import": "./index.js",
|
|
6
|
+
"types": "./index.d.ts"
|
|
7
|
+
},
|
|
8
|
+
"./deterministic-wall-clock": {
|
|
9
|
+
"import": "./deterministic-wall-clock.js",
|
|
10
|
+
"types": "./deterministic-wall-clock.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./wall-clock": {
|
|
13
|
+
"import": "./wall-clock.js",
|
|
14
|
+
"types": "./wall-clock.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"name": "@enormora/wall-clock",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+ssh://git@github.com/enormora/wall-clock.git"
|
|
22
|
+
},
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"type": "module",
|
|
25
|
+
"version": "0.0.1"
|
|
26
|
+
}
|
package/sbom.cdx.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
|
|
3
|
+
"bomFormat": "CycloneDX",
|
|
4
|
+
"specVersion": "1.6",
|
|
5
|
+
"version": 1,
|
|
6
|
+
"metadata": {
|
|
7
|
+
"tools": {
|
|
8
|
+
"components": [
|
|
9
|
+
{
|
|
10
|
+
"type": "application",
|
|
11
|
+
"name": "packtory",
|
|
12
|
+
"version": "0.0.43"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"component": {
|
|
17
|
+
"type": "library",
|
|
18
|
+
"name": "@enormora/wall-clock",
|
|
19
|
+
"version": "0.0.1",
|
|
20
|
+
"bom-ref": "pkg:npm/@enormora/wall-clock@0.0.1",
|
|
21
|
+
"purl": "pkg:npm/@enormora/wall-clock@0.0.1"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"components": [],
|
|
25
|
+
"dependencies": [
|
|
26
|
+
{
|
|
27
|
+
"ref": "pkg:npm/@enormora/wall-clock@0.0.1"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
package/timer-delay.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function validateFiniteDelayInMilliseconds(delayInMilliseconds) {
|
|
2
|
+
if (!Number.isFinite(delayInMilliseconds)) {
|
|
3
|
+
throw new TypeError('Invalid delay, must be a finite number');
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
export function validateTimeoutDelayInMilliseconds(delayInMilliseconds) {
|
|
7
|
+
validateFiniteDelayInMilliseconds(delayInMilliseconds);
|
|
8
|
+
if (delayInMilliseconds < 0) {
|
|
9
|
+
throw new RangeError(`Invalid timeout delay ${delayInMilliseconds}, must be greater than or equal to 0`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function validateIntervalDelayInMilliseconds(delayInMilliseconds) {
|
|
13
|
+
validateFiniteDelayInMilliseconds(delayInMilliseconds);
|
|
14
|
+
if (delayInMilliseconds <= 0) {
|
|
15
|
+
throw new RangeError(`Invalid interval delay ${delayInMilliseconds}, must be greater than 0`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=timer-delay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timer-delay.js","sourceRoot":"","sources":["../../../source/timer-delay.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iCAAiC,CAAC,mBAA2B;IACzE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,SAAS,CAAC,wCAAwC,CAAC,CAAC;IAClE,CAAC;AACL,CAAC;AAED,MAAM,UAAU,kCAAkC,CAAC,mBAA2B;IAC1E,iCAAiC,CAAC,mBAAmB,CAAC,CAAC;IAEvD,IAAI,mBAAmB,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,UAAU,CAAC,yBAAyB,mBAAmB,sCAAsC,CAAC,CAAC;IAC7G,CAAC;AACL,CAAC;AAED,MAAM,UAAU,mCAAmC,CAAC,mBAA2B;IAC3E,iCAAiC,CAAC,mBAAmB,CAAC,CAAC;IAEvD,IAAI,mBAAmB,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,UAAU,CAAC,0BAA0B,mBAAmB,0BAA0B,CAAC,CAAC;IAClG,CAAC;AACL,CAAC"}
|
package/wall-clock.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type WallClock = {
|
|
2
|
+
readonly currentTimestampInMilliseconds: number;
|
|
3
|
+
readonly currentDate: Date;
|
|
4
|
+
readonly setTimeout: <HandlerArguments extends readonly unknown[]>(handler: (...handlerArguments: HandlerArguments) => void, delayInMilliseconds: number, ...handlerArguments: HandlerArguments) => ReturnType<typeof globalThis.setTimeout>;
|
|
5
|
+
readonly clearTimeout: (timeoutIdentifier: ReturnType<typeof globalThis.setTimeout>) => void;
|
|
6
|
+
readonly setInterval: <HandlerArguments extends readonly unknown[]>(handler: (...handlerArguments: HandlerArguments) => void, delayInMilliseconds: number, ...handlerArguments: HandlerArguments) => ReturnType<typeof globalThis.setInterval>;
|
|
7
|
+
readonly clearInterval: (intervalIdentifier: ReturnType<typeof globalThis.setInterval>) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function createWallClock(): WallClock;
|
|
10
|
+
//# sourceMappingURL=wall-clock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wall-clock.d.ts","sourceRoot":"","sources":["../../../source/wall-clock.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IACpB,QAAQ,CAAC,8BAA8B,EAAE,MAAM,CAAC;IAChD,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,CAAC,gBAAgB,SAAS,SAAS,OAAO,EAAE,EAC7D,OAAO,EAAE,CAAC,GAAG,gBAAgB,EAAE,gBAAgB,KAAK,IAAI,EACxD,mBAAmB,EAAE,MAAM,EAC3B,GAAG,gBAAgB,EAAE,gBAAgB,KACpC,UAAU,CAAC,OAAO,UAAU,CAAC,UAAU,CAAC,CAAC;IAC9C,QAAQ,CAAC,YAAY,EAAE,CAAC,iBAAiB,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IAC7F,QAAQ,CAAC,WAAW,EAAE,CAAC,gBAAgB,SAAS,SAAS,OAAO,EAAE,EAC9D,OAAO,EAAE,CAAC,GAAG,gBAAgB,EAAE,gBAAgB,KAAK,IAAI,EACxD,mBAAmB,EAAE,MAAM,EAC3B,GAAG,gBAAgB,EAAE,gBAAgB,KACpC,UAAU,CAAC,OAAO,UAAU,CAAC,WAAW,CAAC,CAAC;IAC/C,QAAQ,CAAC,aAAa,EAAE,CAAC,kBAAkB,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;CACnG,CAAC;AAEF,wBAAgB,eAAe,IAAI,SAAS,CAkB3C"}
|
package/wall-clock.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function createWallClock() {
|
|
2
|
+
return {
|
|
3
|
+
get currentTimestampInMilliseconds() {
|
|
4
|
+
return Date.now();
|
|
5
|
+
},
|
|
6
|
+
get currentDate() {
|
|
7
|
+
return new Date();
|
|
8
|
+
},
|
|
9
|
+
setTimeout: globalThis.setTimeout.bind(globalThis),
|
|
10
|
+
clearTimeout: globalThis.clearTimeout.bind(globalThis),
|
|
11
|
+
setInterval: globalThis.setInterval.bind(globalThis),
|
|
12
|
+
clearInterval: globalThis.clearInterval.bind(globalThis)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=wall-clock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wall-clock.js","sourceRoot":"","sources":["../../../source/wall-clock.ts"],"names":[],"mappings":"AAiBA,MAAM,UAAU,eAAe;IAC3B,OAAO;QACH,IAAI,8BAA8B;YAC9B,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,CAAC;QAED,IAAI,WAAW;YACX,OAAO,IAAI,IAAI,EAAE,CAAC;QACtB,CAAC;QAED,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC;QAElD,YAAY,EAAE,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC;QAEtD,WAAW,EAAE,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC;QAEpD,aAAa,EAAE,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC;KAC3D,CAAC;AACN,CAAC"}
|