@buenos-nachos/time-sync 0.3.2 → 0.4.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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @buenos-nachos/time-sync
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Breaking Changes
6
+
7
+ - 663479e: Removed `isSubscribed` property from context and made all other context properties readonly.
8
+
3
9
  ## 0.3.2
4
10
 
5
11
  ### Patch Changes
@@ -14,7 +20,7 @@
14
20
 
15
21
  ## 0.3.0
16
22
 
17
- ### Minor Changes
23
+ ### Breaking Changes
18
24
 
19
25
  - 122f6c1: Updated `SubscriptionContext.timeSync` type to be readonly and non-nullable, and renamed `SubscriptionContext.isLive` to `SubscriptionContext.isSubscribed`.
20
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buenos-nachos/time-sync",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "author": "Michael Smith <hello@nachos.dev> (https://www.nachos.dev)",
@@ -131,7 +131,6 @@ describe(TimeSync, () => {
131
131
 
132
132
  const expectedCtx: SubscriptionContext = {
133
133
  unsubscribe,
134
- isSubscribed: true,
135
134
  timeSync: sync,
136
135
  intervalLastFulfilledAt: dateAfter,
137
136
  registeredAt: dateBefore,
@@ -563,28 +562,6 @@ describe(TimeSync, () => {
563
562
  const dateAfter = sync.getStateSnapshot().date;
564
563
  expect(dateAfter).not.toEqual(dateBefore);
565
564
  });
566
-
567
- it("Mutates the isLive context value to be false on unsubscribe", async ({
568
- expect,
569
- }) => {
570
- const sync = new TimeSync();
571
-
572
- let ejectedContext: SubscriptionContext | undefined;
573
- const onUpdate = vi.fn((_: unknown, ctx: SubscriptionContext) => {
574
- ejectedContext = ctx;
575
- });
576
-
577
- const unsub = sync.subscribe({
578
- onUpdate,
579
- targetRefreshIntervalMs: refreshRates.oneMinute,
580
- });
581
-
582
- await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
583
- expect(ejectedContext?.isSubscribed).toBe(true);
584
-
585
- unsub();
586
- expect(ejectedContext?.isSubscribed).toBe(false);
587
- });
588
565
  });
589
566
 
590
567
  describe("Subscriptions: context values", () => {
@@ -827,7 +804,6 @@ describe(TimeSync, () => {
827
804
 
828
805
  await vi.advanceTimersByTimeAsync(refreshRates.oneSecond);
829
806
  expect(ejectedContext).toEqual<SubscriptionContext>({
830
- isSubscribed: true,
831
807
  intervalLastFulfilledAt: null,
832
808
  registeredAt: snapBefore,
833
809
  targetRefreshIntervalMs: refreshRates.oneHour,
@@ -840,7 +816,6 @@ describe(TimeSync, () => {
840
816
 
841
817
  const snapAfter = sync.getStateSnapshot().date;
842
818
  expect(ejectedContext).toEqual<SubscriptionContext>({
843
- isSubscribed: true,
844
819
  intervalLastFulfilledAt: snapAfter,
845
820
  registeredAt: snapBefore,
846
821
  targetRefreshIntervalMs: refreshRates.oneHour,
@@ -1086,28 +1061,6 @@ describe(TimeSync, () => {
1086
1061
  await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
1087
1062
  expect(sharedOnUpdate).not.toHaveBeenCalled();
1088
1063
  });
1089
-
1090
- it("Mutates the isLive context value to be false", async ({ expect }) => {
1091
- const sync = new TimeSync();
1092
-
1093
- let ejectedContext: SubscriptionContext | undefined;
1094
- const onUpdate = vi.fn((_: unknown, ctx: SubscriptionContext) => {
1095
- if (ejectedContext === undefined) {
1096
- ejectedContext = ctx;
1097
- }
1098
- });
1099
-
1100
- void sync.subscribe({
1101
- onUpdate,
1102
- targetRefreshIntervalMs: refreshRates.oneMinute,
1103
- });
1104
-
1105
- await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
1106
- expect(ejectedContext?.isSubscribed).toBe(true);
1107
-
1108
- sync.clearAll();
1109
- expect(ejectedContext?.isSubscribed).toBe(false);
1110
- });
1111
1064
  });
1112
1065
 
1113
1066
  /**
package/src/TimeSync.ts CHANGED
@@ -135,9 +135,8 @@ export interface Snapshot {
135
135
  * TimeSync.
136
136
  *
137
137
  * For performance reasons, this object has ZERO readonly guarantees enforced at
138
- * runtime. A few properties are flagged as readonly at the type level, but
139
- * misuse of this value has a risk of breaking a TimeSync instance's internal
140
- * state. Proceed with caution.
138
+ * runtime. All properties are defined as readonly at the type level, but an
139
+ * accidental mutation can still slip through.
141
140
  */
142
141
  export interface SubscriptionContext {
143
142
  /**
@@ -162,12 +161,6 @@ export interface SubscriptionContext {
162
161
  */
163
162
  readonly timeSync: TimeSync;
164
163
 
165
- /**
166
- * Indicates whether the subscription is still live. Will be mutated to be
167
- * false when a subscription is
168
- */
169
- isSubscribed: boolean;
170
-
171
164
  /**
172
165
  * Indicates when the last time the subscription had its explicit interval
173
166
  * "satisfied".
@@ -176,7 +169,7 @@ export interface SubscriptionContext {
176
169
  * the active interval is set to fire every second, you may need to know
177
170
  * which update actually happened five minutes later.
178
171
  */
179
- intervalLastFulfilledAt: ReadonlyDate | null;
172
+ readonly intervalLastFulfilledAt: ReadonlyDate | null;
180
173
  }
181
174
 
182
175
  /**
@@ -439,7 +432,7 @@ export class TimeSync implements TimeSyncApi {
439
432
  // first context in a sub array gets removed by unsubscribing, we
440
433
  // want what was the the second element to still be up to date
441
434
  let shouldCallOnUpdate = true;
442
- for (const context of subs) {
435
+ for (const ctx of subs as readonly Writeable<SubscriptionContext>[]) {
443
436
  // We're not doing anything more sophisticated here because
444
437
  // we're assuming that any systems that can clear out the
445
438
  // subscriptions will handle cleaning up each context, too
@@ -448,17 +441,15 @@ export class TimeSync implements TimeSyncApi {
448
441
  break outer;
449
442
  }
450
443
 
451
- const comparisonDate =
452
- context.intervalLastFulfilledAt ?? context.registeredAt;
444
+ const comparisonDate = ctx.intervalLastFulfilledAt ?? ctx.registeredAt;
453
445
  const isIntervalMatch =
454
- dateTime - comparisonDate.getTime() >=
455
- context.targetRefreshIntervalMs;
446
+ dateTime - comparisonDate.getTime() >= ctx.targetRefreshIntervalMs;
456
447
  if (isIntervalMatch) {
457
- context.intervalLastFulfilledAt = date;
448
+ ctx.intervalLastFulfilledAt = date;
458
449
  }
459
450
 
460
451
  if (shouldCallOnUpdate) {
461
- onUpdate(date, context);
452
+ onUpdate(date, ctx);
462
453
  shouldCallOnUpdate = config.allowDuplicateOnUpdateCalls;
463
454
  }
464
455
  }
@@ -586,7 +577,6 @@ export class TimeSync implements TimeSyncApi {
586
577
  // Have to define this as a writeable to avoid a chicken-and-the-egg
587
578
  // problem for the unsubscribe callback
588
579
  const context: Writeable<SubscriptionContext> = {
589
- isSubscribed: true,
590
580
  timeSync: this,
591
581
  unsubscribe: noOp,
592
582
  registeredAt: new ReadonlyDate(),
@@ -604,7 +594,6 @@ export class TimeSync implements TimeSyncApi {
604
594
  const subsOnSetup = this.#subscriptions;
605
595
  const unsubscribe = (): void => {
606
596
  if (!subscribed || this.#subscriptions !== subsOnSetup) {
607
- context.isSubscribed = false;
608
597
  subscribed = false;
609
598
  return;
610
599
  }
@@ -631,7 +620,6 @@ export class TimeSync implements TimeSyncApi {
631
620
  subscriberCount: Math.max(0, this.#latestSnapshot.subscriberCount - 1),
632
621
  });
633
622
 
634
- context.isSubscribed = false;
635
623
  subscribed = false;
636
624
  };
637
625
  context.unsubscribe = unsubscribe;
@@ -671,12 +659,6 @@ export class TimeSync implements TimeSyncApi {
671
659
  // As long as we clean things the internal state, it's safe not to
672
660
  // bother calling each unsubscribe callback. Not calling them one by
673
661
  // one actually has much better time complexity
674
- for (const subArray of this.#subscriptions.values()) {
675
- for (const ctx of subArray) {
676
- ctx.isSubscribed = false;
677
- }
678
- }
679
-
680
662
  this.#subscriptions.clear();
681
663
 
682
664
  // We swap the map out so that the unsubscribe callbacks can detect