All files / src/crdt VectorClock.ts

100% Statements 27/27
100% Branches 19/19
100% Functions 7/7
100% Lines 22/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70                    64x       40x         7x               9x       9x 9x 14x   9x         19x         19x 19x   19x 33x 33x 33x 33x     19x 16x 13x 8x       106x       9x      
import type { VectorClockMap, ClockRelation } from './types'
 
/**
 * Immutable vector clock — mirrors the Java VectorClock exactly.
 * Keyed by deviceId (string UUID).
 */
export class VectorClock {
  private readonly clock: VectorClockMap
 
  constructor(clock: VectorClockMap = {}) {
    this.clock = { ...clock }
  }
 
  static of(map: VectorClockMap): VectorClock {
    return new VectorClock(map)
  }
 
  /** Increment this device's counter, returning a new VectorClock. */
  increment(deviceId: string): VectorClock {
    return new VectorClock({
      ...this.clock,
      [deviceId]: (this.clock[deviceId] ?? 0) + 1,
    })
  }
 
  /** Merge two clocks by taking the maximum counter per device. */
  merge(other: VectorClock): VectorClock {
    const allDevices = new Set([
      ...Object.keys(this.clock),
      ...Object.keys(other.clock),
    ])
    const merged: VectorClockMap = {}
    for (const device of allDevices) {
      merged[device] = Math.max(this.get(device), other.get(device))
    }
    return new VectorClock(merged)
  }
 
  /** Compare this clock against other — returns causal relationship. */
  compare(other: VectorClock): ClockRelation {
    const allDevices = new Set([
      ...Object.keys(this.clock),
      ...Object.keys(other.clock),
    ])
 
    let thisHasGreater = false
    let otherHasGreater = false
 
    for (const device of allDevices) {
      const a = this.get(device)
      const b = other.get(device)
      if (a > b) thisHasGreater = true
      if (b > a) otherHasGreater = true
    }
 
    if (!thisHasGreater && !otherHasGreater) return 'EQUAL'
    if (thisHasGreater && !otherHasGreater) return 'AFTER'
    if (!thisHasGreater) return 'BEFORE'
    return 'CONCURRENT'
  }
 
  get(deviceId: string): number {
    return this.clock[deviceId] ?? 0
  }
 
  toMap(): VectorClockMap {
    return { ...this.clock }
  }
}