```typescript
/**
 * A fixed-capacity least-recently-used cache.
 */
export class LruCache<K, V> {
  private capacity: number;
  private map: Map<K, Entry<V>>;
  private head: Entry<V>;
  private tail: Entry<V>;

  constructor(capacity: number) {
    if (typeof capacity !== 'number' || capacity <= 0) {
      throw new Error("Capacity must be a positive integer");
    }
    this.capacity = capacity;
    this.map = new Map();
    this.head = { prev: null, key: undefined, value: undefined };
    this.tail = { next: null, key: undefined, value: undefined };
  }

  /**
   * Gets the value associated with the given key from the cache.
   *
   * @param key The key to retrieve
   */
  get(key: K): V | undefined {
    const entry = this.map.get(key);
    if (entry) {
      this.moveForward(entry);
      return entry.value;
    }
    return undefined;
  }

  /**
   * Inserts or updates the value associated with the given key in the cache.
   *
   * @param key The key to insert or update
   * @param value The value to associate with the key
   */
  set(key: K, value: V): void {
    const entry = this.map.get(key);
    if (entry) {
      entry.value = value;
      this.moveForward(entry);
    } else {
      if (this.map.size >= this.capacity) {
        this.removeTail();
      }
      const newEntry = { prev: this.head, key, value };
      this.addNewEntry(newEntry);
    }
  }

  /**
   * Returns the current size of the cache.
   */
  get size(): number {
    return this.map.size;
  }

  private addNewEntry(entry: Entry<V>): void {
    entry.next = this.tail;
    this.tail.prev = entry;
    this.tail = entry;
  }

  private removeTail(): void {
    if (this.head.next === this.tail) {
      throw new Error("Cache is empty");
    }
    const removed = this.tail.prev;
    this.removeEntry(removed);
  }

  private moveForward(entry: Entry<V>): void {
    let current = entry;
    while (current.next !== this.tail) {
      current = current.next;
    }
    if (current === this.head) {
      throw new Error("Key not found");
    }
    const prev = current.prev;
    this.removeEntry(current);
    this.addNewEntry(prev);
  }

  private removeEntry(entry: Entry<V>): void {
    entry.prev.next = entry.next;
    entry.next.prev = entry.prev;
  }
}

/** A single entry in the cache. */
interface Entry<V> {
  prev: { next: Entry<V> | null };
  key: K | null;
  value: V | null;
}
```