Skip to contentcynco

Ledger primitives

@cynco/journals renders journal entries and virtualized registers. @cynco/accounts renders the chart of accounts. Vanilla TypeScript core, React adapters, declarative shadow DOM SSR. Amounts are integer minor units — no floats, anywhere.

v0.1.0-beta.1 · MIT · TypeScript · web components + React

Cash-Maybank — Register

Journal entries, server-rendered

A six-posting payroll run — EPF and SOCSO splits — rendered on the server as declarative shadow DOM and adopted at hydration, with no client re-render. Every color resolves through the @cynco/theme chain: override → role → default, per color scheme.

A register that scales

10,000 seeded entries against one cash account. Fixed row heights reduce windowing to arithmetic — no measurement, no layout thrash — so the register mounts the same number of DOM nodes at 100 rows or 100,000.

Generating 10,000 entries

10,000 entries · register rows · a seeded fixture, so every visit renders byte-identical data.

Comfortable

Payee and narration stacked — two lines per row.

Generating…

Compact

One line per row — twice the rows per viewport.

Generating…

Registers also take a projection-level filter: setFilter reshapes the visible rows in place — group summaries recomputed over the matched rows, matched substrings highlighted — while selection and every public index stay in full-data space.

Reconciliation as conflict resolution

Statement lines left, book postings right, proposed matches as tinted pairs. Accept ✓ or reject ✗ from the center gutter, like resolving a merge conflict. The difference is integer arithmetic and reads reconciled only at exactly zero.

Hover a matched pair to accept or reject it, or accept every exact match at once. Matching is deterministic and 1:1 — read the matching engine docs.

IndicatorStateDescription
proposedSuggested by the matching engine — accept or reject
acceptedCleared against the statement
outstandingIn the books, not yet presented to the bank
missingOn the statement, no book entry — create one

Every edit, diffed like code

Two versions of one entry rendered as an audit-trail diff — word highlights on the narration, tag and link pills added, postings aligned by account and currency with exact before/after amounts. Server-rendered via preloadEntryDiffHTML and adopted at hydration, like every card on this page.

The revised quote splits out a delivery leg — both versions balance to exactly zero, and the diff is pure data from diffEntryVersions.

The chart of accounts as a tree

Canonical colon-delimited paths — Assets:Current:Cash-Maybank — materialize into a keyboard-navigable tree with rolled-up balances and status dots. A file tree with git status, for accounts.

250 entries · rolled-up balances · status dots via setAccountStatus() · search expands ancestors of every match and restores the previous expansion on clear.

Expanded

Every status dot sits on the account that owns it.

Status roll-up

Collapsed groups inherit the highest-severity descendant status.

Honest about imbalance

The store never repairs a broken entry. When postings don't sum to zero per currency, the renderer flags the entry and reports the exact residual.

The cash leg above is exactly RM 1.00 short — rendered, flagged, never repaired.

Built at Cynco

Accounting infrastructure, built the way systems engineers build databases: integer minor units end to end, entries that balance by construction, rendering that holds at six-figure row counts. These packages are the primitives under everything we ship.