@buenos-nachos/time-sync 0.5.5 → 0.6.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/dist/index.d.ts CHANGED
@@ -312,5 +312,4 @@ declare class TimeSync implements TimeSyncApi {
312
312
  clearAll(): void;
313
313
  }
314
314
  //#endregion
315
- export { type Configuration, type InitOptions, type OnTimeSyncUpdate, ReadonlyDate, type Snapshot, type SubscriptionContext, type SubscriptionInitOptions, TimeSync, refreshRates };
316
- //# sourceMappingURL=index.d.ts.map
315
+ export { type Configuration, type InitOptions, type OnTimeSyncUpdate, ReadonlyDate, type Snapshot, type SubscriptionContext, type SubscriptionInitOptions, TimeSync, refreshRates };
package/dist/index.js CHANGED
@@ -468,5 +468,4 @@ var TimeSync = class {
468
468
  };
469
469
 
470
470
  //#endregion
471
- export { ReadonlyDate, TimeSync, refreshRates };
472
- //# sourceMappingURL=index.js.map
471
+ export { ReadonlyDate, TimeSync, refreshRates };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buenos-nachos/time-sync",
3
- "version": "0.5.5",
3
+ "version": "0.6.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "author": "Michael Smith <hello@nachos.dev> (https://www.nachos.dev)",
@@ -16,10 +16,7 @@
16
16
  "ui"
17
17
  ],
18
18
  "files": [
19
- "./src",
20
- "./dist",
21
- "CHANGELOG.md",
22
- "!*.test.ts"
19
+ "./dist"
23
20
  ],
24
21
  "sideEffects": false,
25
22
  "main": "./dist/index.js",
@@ -29,6 +26,6 @@
29
26
  "scripts": {
30
27
  "check:types": "tsc --noEmit"
31
28
  },
32
- "module": "./dist/index.mjs",
29
+ "module": "./dist/index.js",
33
30
  "types": "./dist/index.d.ts"
34
31
  }
package/CHANGELOG.md DELETED
@@ -1,97 +0,0 @@
1
- # @buenos-nachos/time-sync
2
-
3
- ## 0.5.5
4
-
5
- ### Patch Changes
6
-
7
- - 1862a8b: Added explicit build process when preparing NPM scripts to ensure ./dist files can't be omitted
8
-
9
- ## 0.5.4
10
-
11
- ### Patch Changes
12
-
13
- - 3f130f1: updated `files` in package.json to include accidentally removed files
14
-
15
- ## 0.5.3
16
-
17
- ### Patch Changes
18
-
19
- - e401ae4: further updated which files are included in NPM packages
20
-
21
- ## 0.5.2
22
-
23
- ### Patch Changes
24
-
25
- - a2a6843: Removed test files from NPM builds.
26
-
27
- ## 0.5.1
28
-
29
- ### Patch Changes
30
-
31
- - 5fdc201: Updated wording on `Snapshot.date` to be less misleading.
32
-
33
- ## 0.5.0
34
-
35
- ### Breaking Changes
36
-
37
- - c3986e9: revamped all state management and APIs to be based on monotonic time
38
- - c3986e9: Removed `registeredAt` and `intervalLastFulfilledAt` properties from `SubscriptionContext` and added monotonic `registeredAtMs`
39
- - c3986e9: Added monotonic `lastUpdatedAt` property to `Snapshot` type.
40
-
41
- ## 0.4.1
42
-
43
- ### Patch Changes
44
-
45
- - 5f37f1a: refactored class to remove private setSnapshost method
46
-
47
- ## 0.4.0
48
-
49
- ### Breaking Changes
50
-
51
- - 663479e: Removed `isSubscribed` property from context and made all other context properties readonly.
52
-
53
- ## 0.3.2
54
-
55
- ### Patch Changes
56
-
57
- - b8fbaf8: cleanup up comments and types for exported class, methods, and types.
58
-
59
- ## 0.3.1
60
-
61
- ### Patch Changes
62
-
63
- - 5fce018: switched internal implementations to use Date.now more often to reduce memory usage
64
-
65
- ## 0.3.0
66
-
67
- ### Breaking Changes
68
-
69
- - 122f6c1: Updated `SubscriptionContext.timeSync` type to be readonly and non-nullable, and renamed `SubscriptionContext.isLive` to `SubscriptionContext.isSubscribed`.
70
-
71
- ## 0.2.0
72
-
73
- ### Breaking Changes
74
-
75
- - 2f527dd: Changed the default value of `allowDuplicateFunctionCalls` from `false` to `true`
76
-
77
- ### Minor Changes
78
-
79
- - 5f86fac: Added second parameter to `onUpdate` callback. This value is a value of type `SubscriptionContext` and provides information about the current subscription.
80
-
81
- ## 0.1.2
82
-
83
- ### Patch Changes
84
-
85
- - 6189eb2: add README to root directory
86
-
87
- ## 0.1.1
88
-
89
- ### Patch Changes
90
-
91
- - f18d71c: fix: specified module type as ESM
92
-
93
- ## 0.1.0
94
-
95
- ### Minor Changes
96
-
97
- - 8be4b26: Initial release
package/dist/index.cjs DELETED
@@ -1,475 +0,0 @@
1
-
2
- //#region src/ReadonlyDate.ts
3
- /**
4
- * A readonly version of a Date object. To maximize compatibility with existing
5
- * libraries, all methods are the same as the native Date object at the type
6
- * level. But crucially, all methods prefixed with `set` have all mutation logic
7
- * removed.
8
- *
9
- * If you need a mutable version of the underlying date, ReadonlyDate exposes a
10
- * .toNativeDate method to do a runtime conversion to a native/mutable date.
11
- */
12
- var ReadonlyDate = class extends Date {
13
- constructor(initValue, monthIndex, day, hours, minutes, seconds, milliseconds) {
14
- if (initValue instanceof Date && initValue.toString() === "Invalid Date") throw new RangeError("Cannot instantiate ReadonlyDate via invalid date object");
15
- if ([...arguments].some((el) => {
16
- /**
17
- * You almost never see them in practice, but native dates do
18
- * support using negative AND fractional values for instantiation.
19
- * Negative values produce values before 1970.
20
- */
21
- return typeof el === "number" && !Number.isFinite(el);
22
- })) throw new RangeError("Cannot instantiate ReadonlyDate via invalid number(s)");
23
- /**
24
- * This guard clause looks incredibly silly, but we need to do this to
25
- * make sure that the readonly class works properly with Jest, Vitest,
26
- * and anything else that supports fake timers. Critically, it makes
27
- * this possible without introducing any extra runtime dependencies.
28
- *
29
- * Basically:
30
- * 1. We need to make sure that ReadonlyDate extends the Date prototype,
31
- * so that instanceof checks work correctly, and so that the class
32
- * can interop with all libraries that rely on vanilla Dates
33
- * 2. In ECMAScript, this linking happens right as the module is
34
- * imported
35
- * 3. Jest and Vitest will do some degree of hoisting before the
36
- * imports get evaluated, but most of the mock functionality happens
37
- * at runtime. useFakeTimers is NOT hoisted
38
- * 4. A Vitest test file might import the readonly class at some point
39
- * (directly or indirectly), which establishes the link
40
- * 5. useFakeTimers can then be called after imports, and that updates
41
- * the global scope so that when any FUTURE code references the
42
- * global Date object, the fake version is used instead
43
- * 6. But because the linking already happened before the call,
44
- * ReadonlyDate will still be bound to the original Date object
45
- * 7. When super is called (which is required when extending classes),
46
- * the original date object will be instantiated and then linked to
47
- * the readonly instance via the prototype chain
48
- * 8. None of this is a problem when you're instantiating the class by
49
- * passing it actual inputs, because then the date result will always
50
- * be deterministic. The problem happens when you make the date with
51
- * no arguments, because that causes a new date to be created with
52
- * the true system time, instead of the fake system time.
53
- * 9. So, to bridge the gap, we make a separate Date with `new Date()`
54
- * (after it's been turned into the fake version), and then use it to
55
- * overwrite the contents of the real date created with super
56
- */
57
- if (initValue === void 0) {
58
- super();
59
- const overrideForTestCorrectness = Date.now();
60
- super.setTime(overrideForTestCorrectness);
61
- return;
62
- }
63
- if (typeof initValue === "string") {
64
- super(initValue);
65
- if (super.toString() === "Invalid Date") throw new RangeError("Cannot instantiate ReadonlyDate via invalid string");
66
- return;
67
- }
68
- if (monthIndex === void 0) {
69
- super(initValue);
70
- return;
71
- }
72
- if (typeof initValue !== "number") throw new TypeError(`Impossible case encountered: init value has type of '${typeof initValue}, but additional arguments were provided after the first`);
73
- /**
74
- * biome-ignore lint:complexity/noArguments -- Native dates are super
75
- * wonky, and they actually check arguments.length to define behavior
76
- * at runtime. We can't pass all the arguments in via a single call,
77
- * because then the constructor will create an invalid date the moment
78
- * it finds any single undefined value.
79
- */
80
- const argCount = arguments.length;
81
- switch (argCount) {
82
- case 2:
83
- super(initValue, monthIndex);
84
- return;
85
- case 3:
86
- super(initValue, monthIndex, day);
87
- return;
88
- case 4:
89
- super(initValue, monthIndex, day, hours);
90
- return;
91
- case 5:
92
- super(initValue, monthIndex, day, hours, minutes);
93
- return;
94
- case 6:
95
- super(initValue, monthIndex, day, hours, minutes, seconds);
96
- return;
97
- case 7:
98
- super(initValue, monthIndex, day, hours, minutes, seconds, milliseconds);
99
- return;
100
- default: throw new Error(`Cannot instantiate new Date with ${argCount} arguments`);
101
- }
102
- }
103
- toNativeDate() {
104
- const time = super.getTime();
105
- return new Date(time);
106
- }
107
- setDate(_date) {
108
- return super.getTime();
109
- }
110
- setFullYear(_year, _month, _date) {
111
- return super.getTime();
112
- }
113
- setHours(_hours, _min, _sec, _ms) {
114
- return super.getTime();
115
- }
116
- setMilliseconds(_ms) {
117
- return super.getTime();
118
- }
119
- setMinutes(_min, _sec, _ms) {
120
- return super.getTime();
121
- }
122
- setMonth(_month, _date) {
123
- return super.getTime();
124
- }
125
- setSeconds(_sec, _ms) {
126
- return super.getTime();
127
- }
128
- setTime(_time) {
129
- return super.getTime();
130
- }
131
- setUTCDate(_date) {
132
- return super.getTime();
133
- }
134
- setUTCFullYear(_year, _month, _date) {
135
- return super.getTime();
136
- }
137
- setUTCHours(_hours, _min, _sec, _ms) {
138
- return super.getTime();
139
- }
140
- setUTCMilliseconds(_ms) {
141
- return super.getTime();
142
- }
143
- setUTCMinutes(_min, _sec, _ms) {
144
- return super.getTime();
145
- }
146
- setUTCMonth(_month, _date) {
147
- return super.getTime();
148
- }
149
- setUTCSeconds(_sec, _ms) {
150
- return super.getTime();
151
- }
152
- };
153
-
154
- //#endregion
155
- //#region src/TimeSync.ts
156
- /**
157
- * A collection of commonly-needed intervals (all defined in milliseconds).
158
- */
159
- const refreshRates = Object.freeze({
160
- idle: Number.POSITIVE_INFINITY,
161
- halfSecond: 500,
162
- oneSecond: 1e3,
163
- thirtySeconds: 3e4,
164
- oneMinute: 60 * 1e3,
165
- fiveMinutes: 300 * 1e3,
166
- oneHour: 3600 * 1e3
167
- });
168
- /**
169
- * Even though both the browser and the server are able to give monotonic times
170
- * that are at least as precise as a nanosecond, we're using milliseconds for
171
- * consistency with useInterval, which cannot be more precise than a
172
- * millisecond.
173
- */
174
- function getMonotonicTimeMs() {
175
- if (typeof window === "undefined") {
176
- const timeInNanoseconds = process.hrtime.bigint();
177
- return Number(timeInNanoseconds / 1000n);
178
- }
179
- const highResTimestamp = window.performance.now();
180
- return Math.floor(highResTimestamp);
181
- }
182
- /**
183
- * This function is just a convenience for us to sidestep some problems around
184
- * TypeScript's LSP and Object.freeze. Because Object.freeze can accept any
185
- * arbitrary type, it basically acts as a "type boundary" between the left and
186
- * right sides of any snapshot assignments.
187
- *
188
- * That means that if you rename a property a a value that is passed to
189
- * Object.freeze, the LSP can't auto-rename it, and you potentially get missing
190
- * properties. This is a bit hokey, but because the function is defined strictly
191
- * in terms of concrete snapshots, any value passed to this function won't have
192
- * to worry about mismatches.
193
- */
194
- function freezeSnapshot(snap) {
195
- if (!Object.isFrozen(snap.config)) Object.freeze(snap.config);
196
- if (!Object.isFrozen(snap)) Object.freeze(snap);
197
- return snap;
198
- }
199
- const defaultMinimumRefreshIntervalMs = 200;
200
- /**
201
- * One thing that was considered was giving TimeSync the ability to flip which
202
- * kinds of dates it uses, and let it use native dates instead of readonly
203
- * dates. We type readonly dates as native dates for better interoperability
204
- * with pretty much every JavaScript library under the sun, but there is still a
205
- * big difference in runtime behavior. There is a risk that blocking mutations
206
- * could break some other library in other ways.
207
- *
208
- * That might be worth revisiting if we get user feedback, but right now, it
209
- * seems like an incredibly bad idea.
210
- *
211
- * 1. Any single mutation has a risk of breaking the entire integrity of the
212
- * system. If a consumer would try to mutate them, things SHOULD blow up by
213
- * default.
214
- * 2. Dates are a type of object that are far more read-heavy than write-heavy,
215
- * so the risks of breaking are generally lower
216
- * 3. If a user really needs a mutable version of the date, they can make a
217
- * mutable copy first via `const mutable = readonlyDate.toNativeDate()`
218
- *
219
- * The one case when turning off the readonly behavior would be good would be
220
- * if you're on a server that really needs to watch its garbage collection
221
- * output, and you the overhead from the readonly date is causing too much
222
- * pressure on resources. In that case, you could switch to native dates, but
223
- * you'd still need a LOT of trigger discipline to avoid mutations, especially
224
- * if you rely on outside libraries.
225
- */
226
- /**
227
- * TimeSync provides a centralized authority for working with time values in a
228
- * more structured way. It ensures all dependents for the time values stay in
229
- * sync with each other.
230
- *
231
- * (e.g., In a React codebase, you want multiple components that rely on time
232
- * values to update together, to avoid screen tearing and stale data for only
233
- * some parts of the screen.)
234
- */
235
- var TimeSync = class {
236
- /**
237
- * The monotonic time in milliseconds from when the TimeSync instance was
238
- * first instantiated.
239
- */
240
- #initializedAtMs;
241
- /**
242
- * Stores all refresh intervals actively associated with an onUpdate
243
- * callback (along with their associated unsubscribe callbacks).
244
- *
245
- * Supports storing the exact same callback-interval pairs multiple times,
246
- * in case multiple external systems need to subscribe with the exact same
247
- * data concerns. Because the functions themselves are used as keys, that
248
- * ensures that each callback will only be called once per update, no matter
249
- * how subscribers use it.
250
- *
251
- * Each map value should stay sorted by refresh interval, in ascending
252
- * order.
253
- *
254
- * ---
255
- *
256
- * This is a rare case where we actually REALLY need the readonly modifier
257
- * to avoid infinite loops. JavaScript's iterator protocol is really great
258
- * for making loops simple and type-safe, but because subscriptions have the
259
- * ability to add more subscriptions, we need to make an immutable version
260
- * of each array at some point to make sure that we're not iterating through
261
- * values forever
262
- *
263
- * We can choose to do that at one of two points:
264
- * 1. When adding a new subscription
265
- * 2. When dispatching a new round of updates
266
- *
267
- * Because this library assumes that dispatches will be much more common
268
- * than new subscriptions (a single subscription that subscribes for one
269
- * second will receive 360 updates in five minutes), operations should be
270
- * done to optimize that use case. So we should move the immutability costs
271
- * to the subscribe and unsubscribe operations.
272
- */
273
- #subscriptions;
274
- /**
275
- * The latest public snapshot of TimeSync's internal state. The snapshot
276
- * should always be treated as an immutable value.
277
- */
278
- #latestSnapshot;
279
- /**
280
- * A cached version of the fastest interval currently registered with
281
- * TimeSync. Should always be derived from #subscriptions
282
- */
283
- #fastestRefreshInterval;
284
- /**
285
- * Used for both its intended purpose (creating interval), but also as a
286
- * janky version of setTimeout. Also, all versions of setInterval are
287
- * monotonic, so we don't have to do anything special for it.
288
- *
289
- * There are a few times when we need timeout-like logic, but if we use
290
- * setInterval for everything, we have fewer IDs to juggle, and less risk of
291
- * things getting out of sync.
292
- *
293
- * Type defined like this to support client and server behavior. Node.js
294
- * uses its own custom timeout type, but Deno, Bun, and the browser all use
295
- * the number type.
296
- */
297
- #intervalId;
298
- constructor(options) {
299
- const { initialDate, freezeUpdates = false, allowDuplicateOnUpdateCalls = true, minimumRefreshIntervalMs = defaultMinimumRefreshIntervalMs } = options ?? {};
300
- if (!(Number.isInteger(minimumRefreshIntervalMs) && minimumRefreshIntervalMs > 0)) throw new RangeError(`Minimum refresh interval must be a positive integer (received ${minimumRefreshIntervalMs} ms)`);
301
- this.#subscriptions = /* @__PURE__ */ new Map();
302
- this.#fastestRefreshInterval = Number.POSITIVE_INFINITY;
303
- this.#intervalId = void 0;
304
- this.#initializedAtMs = getMonotonicTimeMs();
305
- let date;
306
- if (initialDate instanceof ReadonlyDate) date = initialDate;
307
- else if (initialDate instanceof Date) date = new ReadonlyDate(initialDate);
308
- else date = new ReadonlyDate();
309
- this.#latestSnapshot = freezeSnapshot({
310
- date,
311
- subscriberCount: 0,
312
- lastUpdatedAtMs: null,
313
- config: {
314
- freezeUpdates,
315
- minimumRefreshIntervalMs,
316
- allowDuplicateOnUpdateCalls
317
- }
318
- });
319
- }
320
- #notifyAllSubscriptions() {
321
- const { date, config } = this.#latestSnapshot;
322
- if (config.freezeUpdates || this.#subscriptions.size === 0 || this.#fastestRefreshInterval === Number.POSITIVE_INFINITY) return;
323
- /**
324
- * Two things:
325
- * 1. Even though the context arrays are defined as readonly (which
326
- * removes on the worst edge cases during dispatching), the
327
- * subscriptions map itself is still mutable, so there are a few edge
328
- * cases we need to deal with. While the risk of infinite loops should
329
- * be much lower, there's still the risk that an onUpdate callback could
330
- * add a subscriber for an interval that wasn't registered before, which
331
- * the iterator protocol will pick up. Need to make a local,
332
- * fixed-length copy of the map entries before starting iteration. Any
333
- * subscriptions added during update will just have to wait until the
334
- * next round of updates.
335
- *
336
- * 2. The trade off of the serialization is that we do lose the ability
337
- * to auto-break the loop if one of the subscribers ends up resetting
338
- * all state, because we'll still have local copies of entries. We need
339
- * to check on each iteration to see if we should continue.
340
- */
341
- const subsBeforeUpdate = this.#subscriptions;
342
- const localEntries = Array.from(subsBeforeUpdate);
343
- outer: for (const [onUpdate, subs] of localEntries) for (const ctx of subs) {
344
- if (subsBeforeUpdate.size === 0) break outer;
345
- onUpdate(date, ctx);
346
- if (!config.allowDuplicateOnUpdateCalls) continue outer;
347
- }
348
- }
349
- /**
350
- * The logic that should happen at each step in TimeSync's active interval.
351
- *
352
- * Defined as an arrow function so that we can just pass it directly to
353
- * setInterval without needing to make a new wrapper function each time. We
354
- * don't have many situations where we can lose the `this` context, but this
355
- * is one of them.
356
- */
357
- #onTick = () => {
358
- const { config } = this.#latestSnapshot;
359
- if (config.freezeUpdates) {
360
- clearInterval(this.#intervalId);
361
- this.#intervalId = void 0;
362
- return;
363
- }
364
- this.#latestSnapshot = freezeSnapshot({
365
- ...this.#latestSnapshot,
366
- date: new ReadonlyDate(),
367
- lastUpdatedAtMs: getMonotonicTimeMs() - this.#initializedAtMs
368
- });
369
- this.#notifyAllSubscriptions();
370
- };
371
- #onFastestIntervalChange() {
372
- const fastest = this.#fastestRefreshInterval;
373
- const { lastUpdatedAtMs, config } = this.#latestSnapshot;
374
- if (config.freezeUpdates || this.#subscriptions.size === 0 || fastest === Number.POSITIVE_INFINITY) {
375
- clearInterval(this.#intervalId);
376
- this.#intervalId = void 0;
377
- return;
378
- }
379
- const timeBeforeNextUpdate = fastest - (getMonotonicTimeMs() - (lastUpdatedAtMs ?? this.#initializedAtMs));
380
- clearInterval(this.#intervalId);
381
- if (timeBeforeNextUpdate <= 0) {
382
- this.#onTick();
383
- this.#intervalId = setInterval(this.#onTick, fastest);
384
- return;
385
- }
386
- if (timeBeforeNextUpdate === fastest) {
387
- this.#intervalId = setInterval(this.#onTick, timeBeforeNextUpdate);
388
- return;
389
- }
390
- this.#intervalId = setInterval(() => {
391
- clearInterval(this.#intervalId);
392
- this.#intervalId = setInterval(this.#onTick, fastest);
393
- this.#onTick();
394
- }, timeBeforeNextUpdate);
395
- }
396
- #updateFastestInterval() {
397
- const { config } = this.#latestSnapshot;
398
- if (config.freezeUpdates) {
399
- this.#fastestRefreshInterval = Number.POSITIVE_INFINITY;
400
- return;
401
- }
402
- const prevFastest = this.#fastestRefreshInterval;
403
- let newFastest = Number.POSITIVE_INFINITY;
404
- for (const contexts of this.#subscriptions.values()) {
405
- const subFastest = contexts[0]?.refreshIntervalMs ?? Number.POSITIVE_INFINITY;
406
- if (subFastest < newFastest) newFastest = subFastest;
407
- }
408
- this.#fastestRefreshInterval = newFastest;
409
- if (prevFastest !== newFastest) this.#onFastestIntervalChange();
410
- }
411
- subscribe(options) {
412
- const { targetRefreshIntervalMs, onUpdate } = options;
413
- const { minimumRefreshIntervalMs } = this.#latestSnapshot.config;
414
- if (!(targetRefreshIntervalMs === Number.POSITIVE_INFINITY || Number.isInteger(targetRefreshIntervalMs) && targetRefreshIntervalMs > 0)) throw new Error(`Target refresh interval must be positive infinity or a positive integer (received ${targetRefreshIntervalMs} ms)`);
415
- const subsOnSetup = this.#subscriptions;
416
- let subscribed = true;
417
- const ctx = {
418
- timeSync: this,
419
- registeredAtMs: getMonotonicTimeMs() - this.#initializedAtMs,
420
- refreshIntervalMs: Math.max(minimumRefreshIntervalMs, targetRefreshIntervalMs),
421
- unsubscribe: () => {
422
- try {
423
- if (!subscribed || this.#subscriptions !== subsOnSetup) return;
424
- const contexts = subsOnSetup.get(onUpdate);
425
- if (contexts === void 0) return;
426
- const filtered = contexts.filter((c) => c.unsubscribe !== ctx.unsubscribe);
427
- if (filtered.length === contexts.length) return;
428
- const dropped = Math.max(0, this.#latestSnapshot.subscriberCount - 1);
429
- this.#latestSnapshot = freezeSnapshot({
430
- ...this.#latestSnapshot,
431
- subscriberCount: dropped
432
- });
433
- if (filtered.length > 0) subsOnSetup.set(onUpdate, filtered);
434
- else subsOnSetup.delete(onUpdate);
435
- this.#updateFastestInterval();
436
- } finally {
437
- subscribed = false;
438
- }
439
- }
440
- };
441
- Object.freeze(ctx);
442
- let newContexts;
443
- const prevContexts = subsOnSetup.get(onUpdate);
444
- if (prevContexts !== void 0) newContexts = [...prevContexts, ctx];
445
- else newContexts = [ctx];
446
- subsOnSetup.set(onUpdate, newContexts);
447
- newContexts.sort((c1, c2) => c1.refreshIntervalMs - c2.refreshIntervalMs);
448
- this.#latestSnapshot = freezeSnapshot({
449
- ...this.#latestSnapshot,
450
- subscriberCount: this.#latestSnapshot.subscriberCount + 1
451
- });
452
- this.#updateFastestInterval();
453
- return ctx.unsubscribe;
454
- }
455
- getStateSnapshot() {
456
- return this.#latestSnapshot;
457
- }
458
- clearAll() {
459
- clearInterval(this.#intervalId);
460
- this.#intervalId = void 0;
461
- this.#fastestRefreshInterval = Number.POSITIVE_INFINITY;
462
- this.#subscriptions.clear();
463
- this.#subscriptions = /* @__PURE__ */ new Map();
464
- this.#latestSnapshot = freezeSnapshot({
465
- ...this.#latestSnapshot,
466
- subscriberCount: 0
467
- });
468
- }
469
- };
470
-
471
- //#endregion
472
- exports.ReadonlyDate = ReadonlyDate;
473
- exports.TimeSync = TimeSync;
474
- exports.refreshRates = refreshRates;
475
- //# sourceMappingURL=index.cjs.map