@buenos-nachos/time-sync 0.1.1 → 0.2.0
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/CHANGELOG.md +16 -0
- package/README.md +3 -0
- package/package.json +6 -1
- package/src/TimeSync.test.ts +294 -73
- package/src/TimeSync.ts +218 -115
- package/src/index.ts +2 -1
- package/src/utilities.ts +1 -0
- package/dist/index.d.mts +0 -275
- package/dist/index.d.mts.map +0 -1
- package/dist/index.d.ts +0 -275
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -409
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -406
- package/dist/index.mjs.map +0 -1
package/dist/index.d.mts
DELETED
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
//#region src/ReadonlyDate.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* @file This comment is here to provide clarity on why proxy objects might
|
|
4
|
-
* always be a dead end for this library, and document failed experiments.
|
|
5
|
-
*
|
|
6
|
-
* Readonly dates need to have a lot of interoperability with native dates
|
|
7
|
-
* (pretty much every JavaScript library uses the built-in type). So, this code
|
|
8
|
-
* originally defined them as a Proxy wrapper over native dates. The handler
|
|
9
|
-
* intercepted all methods prefixed with `set` and turned them into no-ops.
|
|
10
|
-
*
|
|
11
|
-
* That got really close to working, but then development ran into a critical
|
|
12
|
-
* limitation of the Proxy API. Basically, if the readonly date is defined with
|
|
13
|
-
* a proxy, and you try to call Date.prototype.toISOString.call(readonlyDate),
|
|
14
|
-
* that immediately blows up because the proxy itself is treated as the receiver
|
|
15
|
-
* instead of the underlying native date.
|
|
16
|
-
*
|
|
17
|
-
* Vitest uses .call because it's the more airtight thing to do in most
|
|
18
|
-
* situations, but proxy objects only have traps for .apply calls, not .call. So
|
|
19
|
-
* there is no way in the language to intercept these calls and make sure
|
|
20
|
-
* they're going to the right place. It is a hard, HARD limitation.
|
|
21
|
-
*
|
|
22
|
-
* The good news, though, is that having an extended class seems like the better
|
|
23
|
-
* option, because it gives us the ability to define custom convenience methods
|
|
24
|
-
* without breaking instanceof checks or breaking TypeScript assignability for
|
|
25
|
-
* libraries that expect native dates. We just have to do a little bit of extra
|
|
26
|
-
* work to fudge things for test runners.
|
|
27
|
-
*/
|
|
28
|
-
/**
|
|
29
|
-
* Any extra methods for readonly dates.
|
|
30
|
-
*/
|
|
31
|
-
interface ReadonlyDateApi {
|
|
32
|
-
/**
|
|
33
|
-
* Converts a readonly date into a native (mutable) date.
|
|
34
|
-
*/
|
|
35
|
-
toNativeDate(): Date;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* A readonly version of a Date object. To maximize compatibility with existing
|
|
39
|
-
* libraries, all methods are the same as the native Date object at the type
|
|
40
|
-
* level. But crucially, all methods prefixed with `set` have all mutation logic
|
|
41
|
-
* removed.
|
|
42
|
-
*
|
|
43
|
-
* If you need a mutable version of the underlying date, ReadonlyDate exposes a
|
|
44
|
-
* .toNativeDate method to do a runtime conversion to a native/mutable date.
|
|
45
|
-
*/
|
|
46
|
-
declare class ReadonlyDate extends Date implements ReadonlyDateApi {
|
|
47
|
-
constructor();
|
|
48
|
-
constructor(initValue: number | string | Date);
|
|
49
|
-
constructor(year: number, monthIndex: number);
|
|
50
|
-
constructor(year: number, monthIndex: number, day: number);
|
|
51
|
-
constructor(year: number, monthIndex: number, day: number, hours: number);
|
|
52
|
-
constructor(year: number, monthIndex: number, day: number, hours: number, seconds: number);
|
|
53
|
-
constructor(year: number, monthIndex: number, day: number, hours: number, seconds: number, milliseconds: number);
|
|
54
|
-
toNativeDate(): Date;
|
|
55
|
-
setDate(_date: number): number;
|
|
56
|
-
setFullYear(_year: number, _month?: number, _date?: number): number;
|
|
57
|
-
setHours(_hours: number, _min?: number, _sec?: number, _ms?: number): number;
|
|
58
|
-
setMilliseconds(_ms: number): number;
|
|
59
|
-
setMinutes(_min: number, _sec?: number, _ms?: number): number;
|
|
60
|
-
setMonth(_month: number, _date?: number): number;
|
|
61
|
-
setSeconds(_sec: number, _ms?: number): number;
|
|
62
|
-
setTime(_time: number): number;
|
|
63
|
-
setUTCDate(_date: number): number;
|
|
64
|
-
setUTCFullYear(_year: number, _month?: number, _date?: number): number;
|
|
65
|
-
setUTCHours(_hours: number, _min?: number, _sec?: number, _ms?: number): number;
|
|
66
|
-
setUTCMilliseconds(_ms: number): number;
|
|
67
|
-
setUTCMinutes(_min: number, _sec?: number, _ms?: number): number;
|
|
68
|
-
setUTCMonth(_month: number, _date?: number): number;
|
|
69
|
-
setUTCSeconds(_sec: number, _ms?: number): number;
|
|
70
|
-
}
|
|
71
|
-
//#endregion
|
|
72
|
-
//#region src/TimeSync.d.ts
|
|
73
|
-
/**
|
|
74
|
-
* A collection of commonly-needed intervals (all defined in milliseconds).
|
|
75
|
-
*/
|
|
76
|
-
declare const refreshRates: Readonly<{
|
|
77
|
-
/**
|
|
78
|
-
* Indicates that a subscriber does not strictly need updates, but is still
|
|
79
|
-
* allowed to be updated if it would keep it in sync with other subscribers.
|
|
80
|
-
*
|
|
81
|
-
* If all subscribers use this update interval, TimeSync will never dispatch
|
|
82
|
-
* any updates.
|
|
83
|
-
*/
|
|
84
|
-
idle: number;
|
|
85
|
-
halfSecond: number;
|
|
86
|
-
oneSecond: number;
|
|
87
|
-
thirtySeconds: number;
|
|
88
|
-
oneMinute: number;
|
|
89
|
-
fiveMinutes: number;
|
|
90
|
-
oneHour: number;
|
|
91
|
-
}>;
|
|
92
|
-
/**
|
|
93
|
-
* The set of readonly options that the TimeSync has been configured with.
|
|
94
|
-
*/
|
|
95
|
-
type Configuration = Readonly<{
|
|
96
|
-
/**
|
|
97
|
-
* Indicates whether the TimeSync instance should be frozen for Snapshot
|
|
98
|
-
* tests.
|
|
99
|
-
*
|
|
100
|
-
* Defaults to false.
|
|
101
|
-
*/
|
|
102
|
-
freezeUpdates: boolean;
|
|
103
|
-
/**
|
|
104
|
-
* The minimum refresh interval (in milliseconds) to use when dispatching
|
|
105
|
-
* interval-based state updates.
|
|
106
|
-
*
|
|
107
|
-
* If a value smaller than this is specified when trying to set up a new
|
|
108
|
-
* subscription, this minimum will be used instead.
|
|
109
|
-
*
|
|
110
|
-
* It is highly recommended that you only modify this value if you have a
|
|
111
|
-
* good reason. Updating this value to be too low and make the event loop
|
|
112
|
-
* get really hot and really tank performance elsewhere in an application.
|
|
113
|
-
*
|
|
114
|
-
* Defaults to 200ms.
|
|
115
|
-
*/
|
|
116
|
-
minimumRefreshIntervalMs: number;
|
|
117
|
-
/**
|
|
118
|
-
* Indicates whether the same `onUpdate` callback (by reference) should be
|
|
119
|
-
* called multiple time if registered by multiple systems.
|
|
120
|
-
*
|
|
121
|
-
* Defaults to false.
|
|
122
|
-
*/
|
|
123
|
-
allowDuplicateOnUpdateCalls: boolean;
|
|
124
|
-
}>;
|
|
125
|
-
/**
|
|
126
|
-
* The set of options that can be used to instantiate a TimeSync.
|
|
127
|
-
*/
|
|
128
|
-
type InitOptions = Readonly<Configuration & {
|
|
129
|
-
/**
|
|
130
|
-
* Indicates whether the TimeSync instance should be frozen for snapshot
|
|
131
|
-
* tests. Highly encouraged that you use this together with
|
|
132
|
-
* `initialDate`.
|
|
133
|
-
*
|
|
134
|
-
* Defaults to false.
|
|
135
|
-
*/
|
|
136
|
-
freezeUpdates: boolean;
|
|
137
|
-
/**
|
|
138
|
-
* The Date object to use when initializing TimeSync to make the
|
|
139
|
-
* constructor more pure and deterministic.
|
|
140
|
-
*/
|
|
141
|
-
initialDate: Date;
|
|
142
|
-
}>;
|
|
143
|
-
/**
|
|
144
|
-
* The callback to call when a new state update is ready to be dispatched.
|
|
145
|
-
*/
|
|
146
|
-
type OnTimeSyncUpdate = (dateSnapshot: ReadonlyDate) => void;
|
|
147
|
-
/**
|
|
148
|
-
* An object used to initialize a new subscription for TimeSync.
|
|
149
|
-
*/
|
|
150
|
-
type SubscriptionOptions = Readonly<{
|
|
151
|
-
/**
|
|
152
|
-
* The maximum update interval that a subscriber needs. A value of
|
|
153
|
-
* Number.POSITIVE_INFINITY indicates that the subscriber does not strictly
|
|
154
|
-
* need any updates (though they may still happen based on other
|
|
155
|
-
* subscribers).
|
|
156
|
-
*
|
|
157
|
-
* TimeSync always dispatches updates based on the lowest update interval
|
|
158
|
-
* among all subscribers.
|
|
159
|
-
*
|
|
160
|
-
* For example, let's say that we have these three subscribers:
|
|
161
|
-
* 1. A - Needs updates no slower than 500ms
|
|
162
|
-
* 2. B – Needs updates no slower than 1000ms
|
|
163
|
-
* 3. C – Uses interval of Infinity (does not strictly need an update)
|
|
164
|
-
*
|
|
165
|
-
* A, B, and C will all be updated at a rate of 500ms. If A unsubscribes,
|
|
166
|
-
* then B and C will shift to being updated every 1000ms. If B unsubscribes
|
|
167
|
-
* after A, updates will pause completely until a new subscriber gets
|
|
168
|
-
* added, and it has a non-infinite interval.
|
|
169
|
-
*/
|
|
170
|
-
targetRefreshIntervalMs: number;
|
|
171
|
-
/**
|
|
172
|
-
* The callback to call when a new state update needs to be flushed amongst
|
|
173
|
-
* all subscribers.
|
|
174
|
-
*/
|
|
175
|
-
onUpdate: OnTimeSyncUpdate;
|
|
176
|
-
}>;
|
|
177
|
-
/**
|
|
178
|
-
* A complete snapshot of the user-relevant internal state from TimeSync. This
|
|
179
|
-
* value is treated as immutable at both runtime and compile time.
|
|
180
|
-
*/
|
|
181
|
-
type Snapshot = Readonly<{
|
|
182
|
-
/**
|
|
183
|
-
* The date that was last dispatched to all subscribers.
|
|
184
|
-
*/
|
|
185
|
-
date: ReadonlyDate;
|
|
186
|
-
/**
|
|
187
|
-
* The number of subscribers registered with TimeSync.
|
|
188
|
-
*/
|
|
189
|
-
subscriberCount: number;
|
|
190
|
-
/**
|
|
191
|
-
* The configuration options used when instantiating the TimeSync instance.
|
|
192
|
-
* The value is guaranteed to be stable for the entire lifetime of TimeSync.
|
|
193
|
-
*/
|
|
194
|
-
config: Configuration;
|
|
195
|
-
}>;
|
|
196
|
-
interface TimeSyncApi {
|
|
197
|
-
/**
|
|
198
|
-
* Subscribes an external system to TimeSync.
|
|
199
|
-
*
|
|
200
|
-
* The same callback (by reference) is allowed to be registered multiple
|
|
201
|
-
* times, either for the same update interval, or different update
|
|
202
|
-
* intervals. Depending on how TimeSync is instantiated, it may choose to
|
|
203
|
-
* de-duplicate these function calls on each round of updates.
|
|
204
|
-
*
|
|
205
|
-
* @throws {RangeError} If the provided interval is not either a positive
|
|
206
|
-
* integer or positive infinity.
|
|
207
|
-
* @returns An unsubscribe callback. Calling the callback more than once
|
|
208
|
-
* results in a no-op.
|
|
209
|
-
*/
|
|
210
|
-
subscribe: (options: SubscriptionOptions) => () => void;
|
|
211
|
-
/**
|
|
212
|
-
* Allows an external system to pull an immutable snapshot of some of the
|
|
213
|
-
* internal state inside TimeSync. The snapshot is frozen at runtime and
|
|
214
|
-
* cannot be mutated.
|
|
215
|
-
*
|
|
216
|
-
* @returns An object with multiple properties describing the TimeSync.
|
|
217
|
-
*/
|
|
218
|
-
getStateSnapshot: () => Snapshot;
|
|
219
|
-
/**
|
|
220
|
-
* Resets all internal state in the TimeSync, and handles all cleanup for
|
|
221
|
-
* subscriptions and intervals previously set up. Configuration values are
|
|
222
|
-
* retained.
|
|
223
|
-
*
|
|
224
|
-
* This method can be used as a dispose method for a locally-scoped
|
|
225
|
-
* TimeSync (a TimeSync with no subscribers is safe to garbage-collect
|
|
226
|
-
* without any risks of memory leaks). It can also be used to reset a global
|
|
227
|
-
* TimeSync to its initial state for certain testing setups.
|
|
228
|
-
*/
|
|
229
|
-
clearAll: () => void;
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* One thing that was considered was giving TimeSync the ability to flip which
|
|
233
|
-
* kinds of dates it uses, and let it use native dates instead of readonly
|
|
234
|
-
* dates. We type readonly dates as native dates for better interoperability
|
|
235
|
-
* with pretty much every JavaScript library under the sun, but there is still a
|
|
236
|
-
* big difference in runtime behavior. There is a risk that blocking mutations
|
|
237
|
-
* could break some other library in other ways.
|
|
238
|
-
*
|
|
239
|
-
* That might be worth revisiting if we get user feedback, but right now, it
|
|
240
|
-
* seems like an incredibly bad idea.
|
|
241
|
-
*
|
|
242
|
-
* 1. Any single mutation has a risk of breaking the entire integrity of the
|
|
243
|
-
* system. If a consumer would try to mutate them, things SHOULD blow up by
|
|
244
|
-
* default.
|
|
245
|
-
* 2. Dates are a type of object that are far more read-heavy than write-heavy,
|
|
246
|
-
* so the risks of breaking are generally lower
|
|
247
|
-
* 3. If a user really needs a mutable version of the date, they can make a
|
|
248
|
-
* mutable copy first via `const mutable = readonlyDate.toNativeDate()`
|
|
249
|
-
*
|
|
250
|
-
* The one case when turning off the readonly behavior would be good would be
|
|
251
|
-
* if you're on a server that really needs to watch its garbage collection
|
|
252
|
-
* output, and you the overhead from the readonly date is causing too much
|
|
253
|
-
* pressure on resources. In that case, you could switch to native dates, but
|
|
254
|
-
* you'd still need a LOT of trigger discipline to avoid mutations, especially
|
|
255
|
-
* if you rely on outside libraries.
|
|
256
|
-
*/
|
|
257
|
-
/**
|
|
258
|
-
* TimeSync provides a centralized authority for working with time values in a
|
|
259
|
-
* more structured way. It ensures all dependents for the time values stay in
|
|
260
|
-
* sync with each other.
|
|
261
|
-
*
|
|
262
|
-
* (e.g., In a React codebase, you want multiple components that rely on time
|
|
263
|
-
* values to update together, to avoid screen tearing and stale data for only
|
|
264
|
-
* some parts of the screen.)
|
|
265
|
-
*/
|
|
266
|
-
declare class TimeSync implements TimeSyncApi {
|
|
267
|
-
#private;
|
|
268
|
-
constructor(options?: Partial<InitOptions>);
|
|
269
|
-
subscribe(sh: SubscriptionOptions): () => void;
|
|
270
|
-
getStateSnapshot(): Snapshot;
|
|
271
|
-
clearAll(): void;
|
|
272
|
-
}
|
|
273
|
-
//#endregion
|
|
274
|
-
export { type Configuration, type InitOptions, type OnTimeSyncUpdate, ReadonlyDate, type Snapshot, type SubscriptionOptions, TimeSync, refreshRates };
|
|
275
|
-
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/ReadonlyDate.ts","../src/TimeSync.ts"],"sourcesContent":[],"mappings":";;;AA8CA;;;;;;;;;ACvCA;AAqBA;AAoCA;;;;;AAuBA;AAKA;AAiCA;;;;;AAgBG;AAsFH;;;UDrMU,eAAA,CC2cK;EAuEM;;;kBD9gBJ;;;;;;;;;;;cAYJ,YAAA,SAAqB,IAAA,YAAgB;;2CAKR;;;;;;kBAgLzB;;;;;;;;;;;;;;;;;;;;AArLjB;;AAqLiB,cC5NJ,YD4NI,EC5NQ,QD4NR,CAAA;EArLiB;;;;;;ACvClC;EAqBY,IAAA,EAAA,MAAA;EAoCA,UAAA,EAAA,MAAW;EACtB,SAAA,EAAA,MAAA;EAec,aAAA,EAAA,MAAA;EAhBW,SAAA,EAAA,MAAA;EAAQ,WAAA,EAAA,MAAA;EAuBtB,OAAA,EAAA,MAAA;AAKZ,CAAA,CAAA;AAiCA;;;AAAuB,KAjGX,aAAA,GAAgB,QAiGL,CAAA;EAAQ;AAgB5B;AAsFH;;;;EA6UqB,aAAA,EAAA,OAAA;EA7UY;;;;;;;;;;;;;;;;;;;;;;;;;KAnKrB,WAAA,GAAc,SACzB;;;;;;;;;;;;;eAec;;;;;KAOH,gBAAA,kBAAkC;;;;KAKlC,mBAAA,GAAsB;;;;;;;;;;;;;;;;;;;;;;;;;YA0BvB;;;;;;KAOC,QAAA,GAAW;;;;QAIhB;;;;;;;;;UAWE;;UAGC,WAAA;;;;;;;;;;;;;;uBAcY;;;;;;;;0BASG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA6DZ,QAAA,YAAoB;;wBA0CV,QAAQ;gBA4NhB;sBAuEM"}
|
package/dist/index.d.ts
DELETED
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
//#region src/ReadonlyDate.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* @file This comment is here to provide clarity on why proxy objects might
|
|
4
|
-
* always be a dead end for this library, and document failed experiments.
|
|
5
|
-
*
|
|
6
|
-
* Readonly dates need to have a lot of interoperability with native dates
|
|
7
|
-
* (pretty much every JavaScript library uses the built-in type). So, this code
|
|
8
|
-
* originally defined them as a Proxy wrapper over native dates. The handler
|
|
9
|
-
* intercepted all methods prefixed with `set` and turned them into no-ops.
|
|
10
|
-
*
|
|
11
|
-
* That got really close to working, but then development ran into a critical
|
|
12
|
-
* limitation of the Proxy API. Basically, if the readonly date is defined with
|
|
13
|
-
* a proxy, and you try to call Date.prototype.toISOString.call(readonlyDate),
|
|
14
|
-
* that immediately blows up because the proxy itself is treated as the receiver
|
|
15
|
-
* instead of the underlying native date.
|
|
16
|
-
*
|
|
17
|
-
* Vitest uses .call because it's the more airtight thing to do in most
|
|
18
|
-
* situations, but proxy objects only have traps for .apply calls, not .call. So
|
|
19
|
-
* there is no way in the language to intercept these calls and make sure
|
|
20
|
-
* they're going to the right place. It is a hard, HARD limitation.
|
|
21
|
-
*
|
|
22
|
-
* The good news, though, is that having an extended class seems like the better
|
|
23
|
-
* option, because it gives us the ability to define custom convenience methods
|
|
24
|
-
* without breaking instanceof checks or breaking TypeScript assignability for
|
|
25
|
-
* libraries that expect native dates. We just have to do a little bit of extra
|
|
26
|
-
* work to fudge things for test runners.
|
|
27
|
-
*/
|
|
28
|
-
/**
|
|
29
|
-
* Any extra methods for readonly dates.
|
|
30
|
-
*/
|
|
31
|
-
interface ReadonlyDateApi {
|
|
32
|
-
/**
|
|
33
|
-
* Converts a readonly date into a native (mutable) date.
|
|
34
|
-
*/
|
|
35
|
-
toNativeDate(): Date;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* A readonly version of a Date object. To maximize compatibility with existing
|
|
39
|
-
* libraries, all methods are the same as the native Date object at the type
|
|
40
|
-
* level. But crucially, all methods prefixed with `set` have all mutation logic
|
|
41
|
-
* removed.
|
|
42
|
-
*
|
|
43
|
-
* If you need a mutable version of the underlying date, ReadonlyDate exposes a
|
|
44
|
-
* .toNativeDate method to do a runtime conversion to a native/mutable date.
|
|
45
|
-
*/
|
|
46
|
-
declare class ReadonlyDate extends Date implements ReadonlyDateApi {
|
|
47
|
-
constructor();
|
|
48
|
-
constructor(initValue: number | string | Date);
|
|
49
|
-
constructor(year: number, monthIndex: number);
|
|
50
|
-
constructor(year: number, monthIndex: number, day: number);
|
|
51
|
-
constructor(year: number, monthIndex: number, day: number, hours: number);
|
|
52
|
-
constructor(year: number, monthIndex: number, day: number, hours: number, seconds: number);
|
|
53
|
-
constructor(year: number, monthIndex: number, day: number, hours: number, seconds: number, milliseconds: number);
|
|
54
|
-
toNativeDate(): Date;
|
|
55
|
-
setDate(_date: number): number;
|
|
56
|
-
setFullYear(_year: number, _month?: number, _date?: number): number;
|
|
57
|
-
setHours(_hours: number, _min?: number, _sec?: number, _ms?: number): number;
|
|
58
|
-
setMilliseconds(_ms: number): number;
|
|
59
|
-
setMinutes(_min: number, _sec?: number, _ms?: number): number;
|
|
60
|
-
setMonth(_month: number, _date?: number): number;
|
|
61
|
-
setSeconds(_sec: number, _ms?: number): number;
|
|
62
|
-
setTime(_time: number): number;
|
|
63
|
-
setUTCDate(_date: number): number;
|
|
64
|
-
setUTCFullYear(_year: number, _month?: number, _date?: number): number;
|
|
65
|
-
setUTCHours(_hours: number, _min?: number, _sec?: number, _ms?: number): number;
|
|
66
|
-
setUTCMilliseconds(_ms: number): number;
|
|
67
|
-
setUTCMinutes(_min: number, _sec?: number, _ms?: number): number;
|
|
68
|
-
setUTCMonth(_month: number, _date?: number): number;
|
|
69
|
-
setUTCSeconds(_sec: number, _ms?: number): number;
|
|
70
|
-
}
|
|
71
|
-
//#endregion
|
|
72
|
-
//#region src/TimeSync.d.ts
|
|
73
|
-
/**
|
|
74
|
-
* A collection of commonly-needed intervals (all defined in milliseconds).
|
|
75
|
-
*/
|
|
76
|
-
declare const refreshRates: Readonly<{
|
|
77
|
-
/**
|
|
78
|
-
* Indicates that a subscriber does not strictly need updates, but is still
|
|
79
|
-
* allowed to be updated if it would keep it in sync with other subscribers.
|
|
80
|
-
*
|
|
81
|
-
* If all subscribers use this update interval, TimeSync will never dispatch
|
|
82
|
-
* any updates.
|
|
83
|
-
*/
|
|
84
|
-
idle: number;
|
|
85
|
-
halfSecond: number;
|
|
86
|
-
oneSecond: number;
|
|
87
|
-
thirtySeconds: number;
|
|
88
|
-
oneMinute: number;
|
|
89
|
-
fiveMinutes: number;
|
|
90
|
-
oneHour: number;
|
|
91
|
-
}>;
|
|
92
|
-
/**
|
|
93
|
-
* The set of readonly options that the TimeSync has been configured with.
|
|
94
|
-
*/
|
|
95
|
-
type Configuration = Readonly<{
|
|
96
|
-
/**
|
|
97
|
-
* Indicates whether the TimeSync instance should be frozen for Snapshot
|
|
98
|
-
* tests.
|
|
99
|
-
*
|
|
100
|
-
* Defaults to false.
|
|
101
|
-
*/
|
|
102
|
-
freezeUpdates: boolean;
|
|
103
|
-
/**
|
|
104
|
-
* The minimum refresh interval (in milliseconds) to use when dispatching
|
|
105
|
-
* interval-based state updates.
|
|
106
|
-
*
|
|
107
|
-
* If a value smaller than this is specified when trying to set up a new
|
|
108
|
-
* subscription, this minimum will be used instead.
|
|
109
|
-
*
|
|
110
|
-
* It is highly recommended that you only modify this value if you have a
|
|
111
|
-
* good reason. Updating this value to be too low and make the event loop
|
|
112
|
-
* get really hot and really tank performance elsewhere in an application.
|
|
113
|
-
*
|
|
114
|
-
* Defaults to 200ms.
|
|
115
|
-
*/
|
|
116
|
-
minimumRefreshIntervalMs: number;
|
|
117
|
-
/**
|
|
118
|
-
* Indicates whether the same `onUpdate` callback (by reference) should be
|
|
119
|
-
* called multiple time if registered by multiple systems.
|
|
120
|
-
*
|
|
121
|
-
* Defaults to false.
|
|
122
|
-
*/
|
|
123
|
-
allowDuplicateOnUpdateCalls: boolean;
|
|
124
|
-
}>;
|
|
125
|
-
/**
|
|
126
|
-
* The set of options that can be used to instantiate a TimeSync.
|
|
127
|
-
*/
|
|
128
|
-
type InitOptions = Readonly<Configuration & {
|
|
129
|
-
/**
|
|
130
|
-
* Indicates whether the TimeSync instance should be frozen for snapshot
|
|
131
|
-
* tests. Highly encouraged that you use this together with
|
|
132
|
-
* `initialDate`.
|
|
133
|
-
*
|
|
134
|
-
* Defaults to false.
|
|
135
|
-
*/
|
|
136
|
-
freezeUpdates: boolean;
|
|
137
|
-
/**
|
|
138
|
-
* The Date object to use when initializing TimeSync to make the
|
|
139
|
-
* constructor more pure and deterministic.
|
|
140
|
-
*/
|
|
141
|
-
initialDate: Date;
|
|
142
|
-
}>;
|
|
143
|
-
/**
|
|
144
|
-
* The callback to call when a new state update is ready to be dispatched.
|
|
145
|
-
*/
|
|
146
|
-
type OnTimeSyncUpdate = (dateSnapshot: ReadonlyDate) => void;
|
|
147
|
-
/**
|
|
148
|
-
* An object used to initialize a new subscription for TimeSync.
|
|
149
|
-
*/
|
|
150
|
-
type SubscriptionOptions = Readonly<{
|
|
151
|
-
/**
|
|
152
|
-
* The maximum update interval that a subscriber needs. A value of
|
|
153
|
-
* Number.POSITIVE_INFINITY indicates that the subscriber does not strictly
|
|
154
|
-
* need any updates (though they may still happen based on other
|
|
155
|
-
* subscribers).
|
|
156
|
-
*
|
|
157
|
-
* TimeSync always dispatches updates based on the lowest update interval
|
|
158
|
-
* among all subscribers.
|
|
159
|
-
*
|
|
160
|
-
* For example, let's say that we have these three subscribers:
|
|
161
|
-
* 1. A - Needs updates no slower than 500ms
|
|
162
|
-
* 2. B – Needs updates no slower than 1000ms
|
|
163
|
-
* 3. C – Uses interval of Infinity (does not strictly need an update)
|
|
164
|
-
*
|
|
165
|
-
* A, B, and C will all be updated at a rate of 500ms. If A unsubscribes,
|
|
166
|
-
* then B and C will shift to being updated every 1000ms. If B unsubscribes
|
|
167
|
-
* after A, updates will pause completely until a new subscriber gets
|
|
168
|
-
* added, and it has a non-infinite interval.
|
|
169
|
-
*/
|
|
170
|
-
targetRefreshIntervalMs: number;
|
|
171
|
-
/**
|
|
172
|
-
* The callback to call when a new state update needs to be flushed amongst
|
|
173
|
-
* all subscribers.
|
|
174
|
-
*/
|
|
175
|
-
onUpdate: OnTimeSyncUpdate;
|
|
176
|
-
}>;
|
|
177
|
-
/**
|
|
178
|
-
* A complete snapshot of the user-relevant internal state from TimeSync. This
|
|
179
|
-
* value is treated as immutable at both runtime and compile time.
|
|
180
|
-
*/
|
|
181
|
-
type Snapshot = Readonly<{
|
|
182
|
-
/**
|
|
183
|
-
* The date that was last dispatched to all subscribers.
|
|
184
|
-
*/
|
|
185
|
-
date: ReadonlyDate;
|
|
186
|
-
/**
|
|
187
|
-
* The number of subscribers registered with TimeSync.
|
|
188
|
-
*/
|
|
189
|
-
subscriberCount: number;
|
|
190
|
-
/**
|
|
191
|
-
* The configuration options used when instantiating the TimeSync instance.
|
|
192
|
-
* The value is guaranteed to be stable for the entire lifetime of TimeSync.
|
|
193
|
-
*/
|
|
194
|
-
config: Configuration;
|
|
195
|
-
}>;
|
|
196
|
-
interface TimeSyncApi {
|
|
197
|
-
/**
|
|
198
|
-
* Subscribes an external system to TimeSync.
|
|
199
|
-
*
|
|
200
|
-
* The same callback (by reference) is allowed to be registered multiple
|
|
201
|
-
* times, either for the same update interval, or different update
|
|
202
|
-
* intervals. Depending on how TimeSync is instantiated, it may choose to
|
|
203
|
-
* de-duplicate these function calls on each round of updates.
|
|
204
|
-
*
|
|
205
|
-
* @throws {RangeError} If the provided interval is not either a positive
|
|
206
|
-
* integer or positive infinity.
|
|
207
|
-
* @returns An unsubscribe callback. Calling the callback more than once
|
|
208
|
-
* results in a no-op.
|
|
209
|
-
*/
|
|
210
|
-
subscribe: (options: SubscriptionOptions) => () => void;
|
|
211
|
-
/**
|
|
212
|
-
* Allows an external system to pull an immutable snapshot of some of the
|
|
213
|
-
* internal state inside TimeSync. The snapshot is frozen at runtime and
|
|
214
|
-
* cannot be mutated.
|
|
215
|
-
*
|
|
216
|
-
* @returns An object with multiple properties describing the TimeSync.
|
|
217
|
-
*/
|
|
218
|
-
getStateSnapshot: () => Snapshot;
|
|
219
|
-
/**
|
|
220
|
-
* Resets all internal state in the TimeSync, and handles all cleanup for
|
|
221
|
-
* subscriptions and intervals previously set up. Configuration values are
|
|
222
|
-
* retained.
|
|
223
|
-
*
|
|
224
|
-
* This method can be used as a dispose method for a locally-scoped
|
|
225
|
-
* TimeSync (a TimeSync with no subscribers is safe to garbage-collect
|
|
226
|
-
* without any risks of memory leaks). It can also be used to reset a global
|
|
227
|
-
* TimeSync to its initial state for certain testing setups.
|
|
228
|
-
*/
|
|
229
|
-
clearAll: () => void;
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* One thing that was considered was giving TimeSync the ability to flip which
|
|
233
|
-
* kinds of dates it uses, and let it use native dates instead of readonly
|
|
234
|
-
* dates. We type readonly dates as native dates for better interoperability
|
|
235
|
-
* with pretty much every JavaScript library under the sun, but there is still a
|
|
236
|
-
* big difference in runtime behavior. There is a risk that blocking mutations
|
|
237
|
-
* could break some other library in other ways.
|
|
238
|
-
*
|
|
239
|
-
* That might be worth revisiting if we get user feedback, but right now, it
|
|
240
|
-
* seems like an incredibly bad idea.
|
|
241
|
-
*
|
|
242
|
-
* 1. Any single mutation has a risk of breaking the entire integrity of the
|
|
243
|
-
* system. If a consumer would try to mutate them, things SHOULD blow up by
|
|
244
|
-
* default.
|
|
245
|
-
* 2. Dates are a type of object that are far more read-heavy than write-heavy,
|
|
246
|
-
* so the risks of breaking are generally lower
|
|
247
|
-
* 3. If a user really needs a mutable version of the date, they can make a
|
|
248
|
-
* mutable copy first via `const mutable = readonlyDate.toNativeDate()`
|
|
249
|
-
*
|
|
250
|
-
* The one case when turning off the readonly behavior would be good would be
|
|
251
|
-
* if you're on a server that really needs to watch its garbage collection
|
|
252
|
-
* output, and you the overhead from the readonly date is causing too much
|
|
253
|
-
* pressure on resources. In that case, you could switch to native dates, but
|
|
254
|
-
* you'd still need a LOT of trigger discipline to avoid mutations, especially
|
|
255
|
-
* if you rely on outside libraries.
|
|
256
|
-
*/
|
|
257
|
-
/**
|
|
258
|
-
* TimeSync provides a centralized authority for working with time values in a
|
|
259
|
-
* more structured way. It ensures all dependents for the time values stay in
|
|
260
|
-
* sync with each other.
|
|
261
|
-
*
|
|
262
|
-
* (e.g., In a React codebase, you want multiple components that rely on time
|
|
263
|
-
* values to update together, to avoid screen tearing and stale data for only
|
|
264
|
-
* some parts of the screen.)
|
|
265
|
-
*/
|
|
266
|
-
declare class TimeSync implements TimeSyncApi {
|
|
267
|
-
#private;
|
|
268
|
-
constructor(options?: Partial<InitOptions>);
|
|
269
|
-
subscribe(sh: SubscriptionOptions): () => void;
|
|
270
|
-
getStateSnapshot(): Snapshot;
|
|
271
|
-
clearAll(): void;
|
|
272
|
-
}
|
|
273
|
-
//#endregion
|
|
274
|
-
export { type Configuration, type InitOptions, type OnTimeSyncUpdate, ReadonlyDate, type Snapshot, type SubscriptionOptions, TimeSync, refreshRates };
|
|
275
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/ReadonlyDate.ts","../src/TimeSync.ts"],"sourcesContent":[],"mappings":";;;AA8CA;;;;;;;;;ACvCA;AAqBA;AAoCA;;;;;AAuBA;AAKA;AAiCA;;;;;AAgBG;AAsFH;;;UDrMU,eAAA,CC2cK;EAuEM;;;kBD9gBJ;;;;;;;;;;;cAYJ,YAAA,SAAqB,IAAA,YAAgB;;2CAKR;;;;;;kBAgLzB;;;;;;;;;;;;;;;;;;;;AArLjB;;AAqLiB,cC5NJ,YD4NI,EC5NQ,QD4NR,CAAA;EArLiB;;;;;;ACvClC;EAqBY,IAAA,EAAA,MAAA;EAoCA,UAAA,EAAA,MAAW;EACtB,SAAA,EAAA,MAAA;EAec,aAAA,EAAA,MAAA;EAhBW,SAAA,EAAA,MAAA;EAAQ,WAAA,EAAA,MAAA;EAuBtB,OAAA,EAAA,MAAA;AAKZ,CAAA,CAAA;AAiCA;;;AAAuB,KAjGX,aAAA,GAAgB,QAiGL,CAAA;EAAQ;AAgB5B;AAsFH;;;;EA6UqB,aAAA,EAAA,OAAA;EA7UY;;;;;;;;;;;;;;;;;;;;;;;;;KAnKrB,WAAA,GAAc,SACzB;;;;;;;;;;;;;eAec;;;;;KAOH,gBAAA,kBAAkC;;;;KAKlC,mBAAA,GAAsB;;;;;;;;;;;;;;;;;;;;;;;;;YA0BvB;;;;;;KAOC,QAAA,GAAW;;;;QAIhB;;;;;;;;;UAWE;;UAGC,WAAA;;;;;;;;;;;;;;uBAcY;;;;;;;;0BASG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA6DZ,QAAA,YAAoB;;wBA0CV,QAAQ;gBA4NhB;sBAuEM"}
|