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

coc-gustave

v0.2.0

Published

Contract hovers for gustave specs in coc.nvim, including under tsgo

Readme

coc-gustave

Contract hovers for gustave specs in coc.nvim — at call sites, across files, and under tsgo.

const divide: (a: number, b: number) => number     ← from your language server

Division                                           ← appended by this extension
require:
  • divisor is nonzero
ensure:
  • quotient times divisor equals dividend

Why this exists

gustave ships gustave/ts-plugin, which does the same job. But a TypeScript language service plugin needs a language service to host it, and the two setups a coc user is likely to be running can't:

  • tsgo / TypeScript 7 has no plugin API at all.
  • coc-tsserver doesn't pass --allowLocalPluginLoads, so tsserver won't resolve a plugin from the project's own tsconfig.json.

This extension answers the same question out of process. It registers a second hover provider; coc's HoverManager runs every registered provider and concatenates the results, so whatever your language server said stands and the contract is appended beneath it. It doesn't replace, wrap, or proxy your language server, and it works the same whether that's tsgo, tsserver, or nothing at all.

Install

:CocInstall coc-gustave

That's it. gustave itself is a dependency of the extension, so it works even in a project that doesn't have it installed.

Confirm with :CocList extensions.

From a checkout

For hacking on the extension, put the folder on your runtimepath instead. With lazy.nvim:

{ dir = "/absolute/path/to/gustave/coc-extension", name = "coc-gustave" }

vim-plug, or by hand:

Plug '/absolute/path/to/gustave/coc-extension'
" or
set runtimepath+=/absolute/path/to/gustave/coc-extension

It appears in :CocList extensions marked [RTP]. Build it once, linking the sibling gustave rather than the published one:

cd .. && npm link
cd coc-extension && npm install --ignore-scripts && npm link gustave && npm run build

(The npm link dance is only needed while the gustave version this depends on is unpublished; after that a plain npm install does it.)

If you also use VS Code on the same project, keep the gustave/ts-plugin entry in tsconfig.json. The two never both apply: nothing loads the plugin in the setups this extension is for.

How the lookup works

Syntactic, not type-directed. On hover it finds the identifier under the cursor, follows imports and re-exports with ts.resolveModuleName — so your tsconfig.json paths and baseUrl are honored — parses the single file that declares the spec, walks the spec<...>() chain, and collects the condition names.

No Program, no type checker, no second tsserver-sized process. If you moved to tsgo for speed, this doesn't hand the cost back: it's one file parse per hop, and open buffers are read from the buffer rather than from disk, so hovers are right on unsaved edits.

One thing it does better than the type-directed plugin: factory-form conditions.

.require((from, to, cents) => ({
  "amount is positive": () => cents > 0,
  "accounts are distinct": () => from.id !== to.id,
}))

Those names never reach the Contracted brand, so the ts-plugin can't show them and describe() can't list them without a live call. A parse reads them straight off the object literal.

Contracts from other packages

A published package ships dist, not sources, so there is no chain to walk. Declaration emit writes the condition names into the Contracted brand as literal types:

export declare const settleInvoice: Contracted<(cents: number) => number, {
  pre: "amount is positive" | "amount is an integer";
  post: "never settles more than it was given";
}>;

So when resolution lands on a .d.ts, the names are read off the type annotation instead — the same thing the ts-plugin reads, reached by parse rather than by checker. A shared contracts library hovers like local code. The one thing that doesn't survive the boundary is .named("..."), which the brand doesn't carry, so those contracts report their conditions without a heading.

What's left as a miss is anything a parse can't follow: a spec reached through something other than a direct binding or import. That shows no contract rather than a guess, and your language server's hover is untouched.

Auditing the whole project

:CocCommand gustave.checkProject

Answers, once, whether anything in the project provably violates a precondition. It audits the nearest tsconfig.json above the current buffer (pass a path to override), fills the quickfix list so you can walk the hits with :cnext, and says nothing more than a message when it's clean — a window that opens to announce the absence of problems is its own problem.

The work happens in a subprocess. coc's extension host is single-threaded, so building a TypeScript program inside it would freeze the editor for as long as the project takes, and the first time that happens is the last time anyone runs the command.

The summary is a ratio on purpose: "no violations provable across 316 contracted call sites" is a much weaker claim than "316 calls are correct". Most call sites pass arguments no static analysis can pin down, and those are skipped silently. It's the same engine as the gustave/eslint rule, and the same gustave check you'd run in CI — which is where this question is worth asking automatically rather than on demand.

Configuration

| Setting | Default | Effect | | --- | --- | --- | | gustave.hover.enable | true | Append contract conditions to hovers. | | gustave.hover.showName | true | Head the hover with the contract's .named("..."). |