# pn-counter _(no submodules)_ ## Purpose Provides `PNCounter`, a positive-negative counter CRDT that supports both increment and decrement operations. Built from two G-Counters (one for increments, one for decrements), the net value is `sum(increments) - sum(decrements)`. All CRDT invariants (commutativity, associativity, idempotency) are preserved. ## Features - `increment(nodeId, delta?)` — adds to the increment counter for `nodeId` - `decrement(nodeId, delta?)` — adds to the decrement counter for `nodeId` - `total()` — net value: sum of all increments minus sum of all decrements - `merge(state)` — element-wise maximum on both increment and decrement counters - `toJSON()` / `fromJSON()` — serialization round-trip - `clone()` — deep copy ## Usage: Basic Example ```ts import { PNCounter } from "@suaveplan/crdt"; const counter = new PNCounter(); counter.increment("n1", 10); counter.decrement("n1", 3); console.log(counter.total()); // 7 ``` ## Usage: Advanced Example ```ts import { PNCounter } from "@suaveplan/crdt"; const a = new PNCounter("likes"); a.increment("user-1", 5); const b = new PNCounter("likes"); b.decrement("user-2", 2); a.merge(b.state()); console.log(a.total()); // 3 (5 - 2) ``` ## API Reference ### `PNCounter` - `increment(nodeId, delta?)` — throws `OPERATION_INVALID` if delta ≤ 0 - `decrement(nodeId, delta?)` — throws `OPERATION_INVALID` if delta ≤ 0 - `total(): number` — net counter value ## Implementation Notes The value can be negative if decrements exceed increments. This is intentional — PNCounter does not enforce a floor of 0.