@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 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
@@ -0,0 +1,3 @@
1
+ export { createWallClock } from "./wall-clock.js";
2
+ export { createDeterministicWallClock } from "./deterministic-wall-clock.js";
3
+ //# sourceMappingURL=index.js.map
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"}
@@ -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"}