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

@sevn/elections

v1.0.0

Published

Typed client for the Brazilian 2026 election results CDN. Builds the path, does the GET, returns typed JSON.

Readme

@sevn/elections

Typed reader for the 2026 Brazilian election results CDN.

The data is a set of static JSON files. This package builds the path, does the GET, and hands back typed JSON — no server state, no writes, no auth. What it buys you over a raw fetch:

  1. Paths are correct by construction. You never type a route.
  2. Impossible combinations don't compile. The office × scope matrix is in the types.
  3. Caching and request deduplication come built in, via @sevn/reqcache.

It deliberately does not poll, subscribe, sort, translate, or compute percentages. When to re-read is your application's decision.

Install

npm install @sevn/elections

Node 18+, Bun, Deno and modern browsers. All four are covered by the smoke tests.

Usage

import { Elections } from '@sevn/elections';

const elections = new Elections({ clientId: 'your-id', year: 2026 });

const national = await elections.results({ office: 'president', round: 1, scope: 'br' });
const saoPaulo = await elections.results({ office: 'governor', round: 1, scope: 'uf', state: 'SP' });

CommonJS works the same way:

const { Elections } = require('@sevn/elections');

From a plain <script> tag, the UMD build exposes a SevnElections global with @sevn/reqcache already bundled in — nothing else to load:

<script src="https://cdn.jsdelivr.net/npm/@sevn/elections"></script>
<script>
  const elections = new SevnElections.Elections({ clientId: 'your-id' });
  elections.results({ office: 'president', round: 1, scope: 'br' }).then(console.log);
</script>

CORS

The origin does not currently send CORS headers, and its preflight answers 403. In practice:

  • Server-side works — SSR, a BFF, or any backend.
  • Browser-side works when baseUrl points at your own proxy.
  • There is no workaround built in, and there won't be one: no JSONP, no bundled proxy, no no-cors.

Nothing in your code changes if the origin later opens up.

Configuration

| Option | Type | Default | What it does | | --- | --- | --- | --- | | clientId | string | — required¹ | Becomes the subdomain of the origin. | | year | number | 2026 | Year used in paths. | | baseUrl | string | from clientId | A complete alternative origin. Wins over clientId. | | cache | boolean | true | Local caching. | | cacheTTL | number (ms) | 10_000 | How long a response stays fresh. | | storage | 'memory' \| 'local' \| 'session' \| CacheStorageAdapter | 'local' in a browser, 'memory' on a server | Where the cache lives. | | maxEntries | number | 200 | Cached paths before least-recently-used eviction. | | monotonic | boolean | false | Discard a response whose count went backwards. | | debug | boolean | false | Log every cache decision. | | fetch | typeof fetch | globalThis.fetch | Alternative implementation, for SSR, tests or retries. |

¹ Not required when baseUrl is given. Passing both: baseUrl wins.

Every call also accepts year, cache, cacheTTL, monotonic and signal, scoped to that one call.

The functions

| Function | Offices | Scopes | | --- | --- | --- | | results | all | br¹, uf, mun | | countingProgress | — takes no office | br, uf | | byState | all | — takes no scope | | byMunicipality | all | uf | | byParty | legislative only | uf | | timeline | majoritarian only | br¹, uf | | headToHead | majoritarian only | br¹, uf | | comparison | majoritarian only | br¹, uf — takes no round | | municipalities | — | uf | | states | — | — | | parties | — | — |

¹ br exists only for president.

Three rules, all enforced at compile time and at runtime (for values arriving from a dropdown, where the compiler can't help):

elections.results({ office: 'governor', round: 1, scope: 'br' });   // ✗ br is president-only
elections.byParty({ office: 'president', round: 1, state: 'SP' });  // ✗ legislative only
elections.timeline({ office: 'senator', round: 1, scope: 'uf', state: 'SP' }); // ✗ majoritarian only

At runtime those throw ElectionsScopeError, whose allowed field lists what would have worked.

Return types follow the scope, so you never narrow by hand:

const national = await elections.countingProgress({ round: 1, scope: 'br' });
national.regions;  // ✗ compile error — the national object has no region breakdown

const state = await elections.countingProgress({ round: 1, scope: 'uf', state: 'SP' });
state.regions;     // ✓ CountingRegionMunicipality[]

The Exterior (ZZ)

Votes cast abroad are published for president (results, timeline, comparison) and for countingProgress. ZZ is deliberately kept out of the UF union — it is accepted only where the origin actually publishes it, and rejected everywhere else before a request is spent.

Errors

| Class | When | | --- | --- | | ElectionsNotPublishedError | HTTP 404 — a valid path whose object isn't published yet. | | ElectionsScopeError | An office × scope combination that doesn't exist, caught before the network. | | ElectionsNetworkError | Transport failure, timeout, or a non-404 status with nothing cached. | | ElectionsError | Base class for all of the above. |

A 404 is an ordinary state during a count, not an outage: head-to-head exists for a handful of states, municipal results only where reporting has started, and the second round doesn't exist until there is one. The origin serves HTML in a 404 body, so the status is always checked before anything parses JSON.

An aborted signal propagates AbortError unwrapped.

Caching

On by default, 10 seconds, matching the edge's s-maxage. Concurrent calls to the same path share one request. When a refresh fails and a cached value exists, the cached value is served — on election night, a screen showing data from 30 seconds ago beats a broken screen.

Reference objects (municipalities, states, parties) get a one-hour TTL automatically; you don't configure it.

On a server, each process has its own cache. The TTL is per process, and raising cacheTTL will not make it shared — that needs a shared store, not a longer timeout.

The monotonic rule

Counts only move forward. If a response arrives with a smaller count than the one already held, it's an older copy that reached you late, and showing it makes the tally run backwards on screen. Turn monotonic: true on to discard those.

It's off by default because it trades freshness for stability: a legitimately new response whose count was revised downward is discarded too.

It does not cover every object. The check is numeric, and two objects have no counter to check:

| Object | Field watched | | --- | --- | | results | summary.ballots_counted | | countingProgress | sections_counted | | comparison | round_1_summary.total_valid_votes | | timeline | number of snapshots | | headToHead | number of points | | byState, byMunicipality | none — the rule has no effect here |

byState and byMunicipality only carry last_updated, a date string, so turning the rule on does nothing for them. If a state map running backwards matters to you, compare last_updated yourself.

Divergences from the written specification

The type definitions were built from the live origin, not from prose. Where the two disagreed, the data won. If you are working from an older spec document, these are the differences:

| The spec described | The origin actually serves | | --- | --- | | byMunicipality → a candidates[] array per municipality | absent — only leader and runner_up | | municipalities[].municipality_slug | absent | | resultslast_updated, state_name, municipality_name, municipality_slug, municipality_tse_code | absent — the timestamp lives at summary.last_updated | | countingProgress at brregions with one entry per state | absent entirely; only the state-scope object has regions, keyed by municipality | | timelinecandidates_meta[].photo_url | absent | | — | results.candidates[].will_go_to_2_turn, which the spec didn't mention | | the provenance envelope on every counting object | only on results and countingProgress |

Also worth knowing: scope_type reports mu while the path segment is mun, and scope_code is lowercase while the path is uppercase. Paths are case-sensitive with no redirects — uf/sp is a 404.

Development

npm run typecheck        # tsc, including the compile-time test assertions
npm test                 # offline suite, no server needed
npm run build            # ESM + CJS + UMD + a single .d.ts

SEVN_TEST_ORIGIN=http://localhost:8091 npm run capture-fixtures
SEVN_TEST_ORIGIN=http://localhost:8091 npm run test:live

The offline suite runs against committed fixtures and needs no network. The contract suite is opt-in and asserts shape, never values.

Licence

MIT