npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

finidb

v0.1.9

Published

A BI-style calculation engine for AI agents: line items x periods, conditional rules, incremental recalculation. Free for noncommercial and educational use; commercial license required.

Downloads

1,163

Readme

finidb

A BI-style calculation engine for AI agents. Line items × time periods, conditional rules, incremental recalculation. Use it instead of generating a spreadsheet when the task is a forecast, budget, plan, financial statement, or any model a human will later open and adjust.

| Your task | Use | |---|---| | Clean, join, or reshape data | Pandas / DuckDB | | Query an existing warehouse | SQL | | A static chart of static data | matplotlib | | Line items × periods with formulas; a forecast, budget or plan; a ledger rolled up into something that stays live | finidb |

Twelve lines, a whole forecast

import { FiniDB } from 'finidb'

const f = new FiniDB()
f.createModel('nvda')
f.createPeriods('nvda', 'periods', { start: '2024-01', count: 6, grain: 'year', histUntil: '2026-12-31' })
f.createTable('nvda', 'lines', [{ id: 'name' }, { id: 'category' }], { rows: [
  { id: 'revenue', category: 'flow' }, { id: 'cogs', category: 'flow' }, { id: 'rnd', category: 'opex' },
  { id: 'sga', category: 'opex' }, { id: 'gross_profit', category: 'flow' }, { id: 'opex', category: 'flow' }, { id: 'ebit', category: 'flow' } ] })
f.createTable('nvda', 'financials', [{ id: 'account' }, { id: 'period', ref: 'periods' }, { id: 'amount', type: 'number' }], { rows: [/* …from the filing… */] })
f.createPivot('nvda', 'assumptions', { dims: [{ id: 'driver', table: 'drivers' }, { id: 'period', table: 'periods' }], lineDim: 'driver' })
f.createPivot('nvda', 'income_statement', { dims: [{ id: 'line', table: 'lines' }, { id: 'period', table: 'periods' }], lineDim: 'line', timeDim: 'period' })
f.setRules('nvda', 'income_statement', `
  revenue[frame=hist] = SUM(financials.amount[account=revenue])
  revenue[frame=fcst] = PREV(revenue) * (1 + assumptions.revenue_growth)
  cogs[frame=fcst]    = revenue * assumptions.cogs_pct
  gross_profit        = revenue - cogs
  opex                = SUM(value[line.category = opex])
  ebit                = gross_profit - opex
`)
console.log(f.query('nvda', { table: 'income_statement', rows: ['line'], cols: ['period'] }))
| line         | FY2024 |  FY2025 |  FY2026 |  FY2027 |  FY2028 |  FY2029 |
|--------------|-------:|--------:|--------:|--------:|--------:|--------:|
| Revenue      | 60,922 | 130,500 | 180,000 | 252,000 | 352,800 | 493,920 |
| COGS         | 16,621 |  32,639 |  45,000 |  63,000 |  88,200 | 123,480 |
| Gross Profit | 44,301 |  97,861 | 135,000 | 189,000 | 264,600 | 370,440 |

The language in one screen

Revenue[frame=fcst] = PREV(Revenue) * (1 + growth)      // a rule: target[condition] = expression
GrossProfit         = Revenue - COGS                     // line items are nouns
points              = SUM(activities.score)              // a column of a table is a set; SUM groups it by this pivot's dims
score               = activity_type.score                // a dot follows a reference
total[tranche=all]  = SUM(value[tranche != total])       // member masks
sum_of_subs         = SUM(goal[region.parent = @region]) // @dim is the current cell's member
balance             = balance[row-1] + amount            // tabular running total

One reference syntax: a name with an optional [selector]. [period-1], [period=first..this], [line in (a, b)], [period.year >= 2027], [rep.territory = @territory]. Every reference is statically classifiable, which is what lets the engine recompute only what changed.

Excel export

Every model exports as an .xlsx whose cells hold live formulas compiled from the rules, one sheet per pivot (line items down, periods across) and per table, inputs in blue and formulas in black, plus a Rules sheet. PREV(x) becomes the cell to the left, a cross-statement reference becomes a sheet reference, SUM(ledger.amount) becomes SUMIFS over the table sheet, type.score becomes INDEX/MATCH. Iterative calculation carries over as the workbook setting. Cells whose rule has no faithful Excel form keep their value and are listed on a Notes sheet.

npx finidb build model.json                        # writes model.xlsx next to the document (--no-xlsx to skip)
npx finidb export --data-dir ./data --out m.xlsx   # from a database

Over REST: GET /db/:db/export.xlsx. In code: f.exportXlsx(model). The test suite evaluates the exported formulas with an independent spreadsheet engine and checks every cell against the engine.

Circular references

Same-period circularities (interest on the average debt balance, a revolver that keeps cash above a minimum) are #CYCLE by default. Turn on iterative calculation per model and the engine iterates the cycle to a fixed point the way Excel's iterative calculation does:

f.setIterate('m', true);                                   // 100 passes, tolerance 0.001 (Excel's defaults)
f.setIterate('m', { maxIterations: 200, tolerance: 1e-6 });

In a model document: "iterate": true. Over REST: PATCH /db/:db/models/:model { "iterate": true }. Both engines iterate only the cells that actually read a provisional value, so the rest of the model is computed once. A cycle that does not settle within the cap returns #ITER.

Build a model from a document (no server needed)

npx finidb build model.json          # prints the statements; examples/ has tested documents: dcf, comparables,
                                     # precedents, salesops, budget-vs-actual, ledger-to-model, scenarios, coreweave

The document (periods, pivots with line items, historical inputs and rules, tables, outputs, and optionally dashboards: an editable assumptions table plus KPIs and charts, built when the document is opened on finicast.com, bars for amounts and lines for rates chosen from each line's format when a card gives no type, a theme for the audience (research, banking, revenue, controller, boardroom, print), a links card for navigation across several dashboards, and a table card's extra columns for a text commentary measure beside the numbers) is the same one finicast.com accepts at POST https://finicast.com/api/build. finidb build also prints a link of the form https://finicast.com/import#m=… with the model deflate-compressed in the URL fragment: opening it builds a live, editable workspace from the browser, so an agent in a sandbox that can only reach npm can still hand the user a link (modelLink(doc) in the API). Pasting the document at https://finicast.com/import does the same by hand.

Status

Design in ../docs/design. Implemented (first cut of milestones M1–M5):

  • Engine: column store, schema, parser, 100+ functions, PERIOD(date), the reference evaluator (the oracle) and the incremental engine (dense per-column storage, recorded dependencies, row-level dirty sets propagated through reference columns, integer-bucket delta aggregates, tier-2 compiled row rules). A differential test runs random edit sequences through both engines and compares every cell.
  • Persistence: JSONL oplog with content-addressed blobs for bulk loads, binary snapshots of inputs only, openDatabase(dir) (snapshot + log tail).
  • Server + CLI: finidb serve (node:http, multi-database, users/grants with scrypt, Basic and bearer auth, the REST routes of doc 07, long-poll /changes, CSV load with append / upsert / replace and a dry run, linked tables fetched from an API (source, the fmp preset, /refresh), batch), finidb createdb | createuser | grant | query | rules | load | bench | build.
  • MCP + skill: nine tools (src/mcp), finidb mcp for stdio, finidb skill prints skill/SKILL.md (syntax, recipes and error references); finidb skill --install <dir> copies the whole skill folder.
  • Model documents: finidb build model.json applies a JSON model document (periods, pivots with lines, inputs and rules, tables) in-process and prints the requested pivots as markdown or JSON — no server needed. Pivots may reference each other across periods (income statement, cash flow and balance sheet in separate pivots); the engine resolves the cross-pivot recurrence cell by cell.
npm test                              # 49 tests: unit, models, differential, persistence, server, MCP
node --import tsx bench/run.ts 100000 # B1 sales-ops benchmark
npm run build && node dist/cli/finidb.js serve --data-dir ./data
FINIDB_DIR=./data npx finidb mcp      # MCP over stdio, persistent
npx finidb build examples/coreweave.json   # model document → markdown statements

B1, 100,000 activities, incremental engine, measured 2026-09-15 on a shared host with a load average above 5 (treat as upper bounds):

| Case | p50 | |---|---| | one activity's type changes → territory pivot read | 2–5 ms | | one activity's rep changes | 2–4 ms | | one rep moves territory (~500 rows) | 3–4 ms | | one scoring value changes (~25k rows recomputed + delta) | 23–35 ms | | reorient the view (no recompute) | < 1 ms |

Known gaps: pivot measure columns recompute whole-column on any change (fine at 10³–10⁴ cells, not yet at 10⁵+); no worker-per-database; /batch is not atomic; no delete routes beyond dropTable/dropModel in the facade; the web workspace (M6–M8) is not started.

License

Free for personal, educational, research and other noncommercial use under the PolyForm Noncommercial License 1.0.0. Commercial use requires a paid license from Finicast, Inc.; see COMMERCIAL-LICENSE.md or write to [email protected].