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

@tegmentum/ducklink

v0.5.0

Published

Ergonomic JS/TS API for DuckDB-wasm in the browser: create -> connect -> query typed Result rows, conformance-gated extension loading, and result streaming. Runs in any modern browser via a JSPI fast-path (Chrome 137+) with an automatic non-JSPI Web Worke

Readme

@tegmentum/ducklink

Ergonomic JS/TS API for DuckDB-wasm in the browser: create -> connect -> query returning typed Result rows (JS objects, BigInt-aware), conformance-gated extension loading, and result streaming. Built on @tegmentum/datalink-browser (the DB-agnostic runtime plumbing) + the DuckDB core wasm component + the ducklink registry catalog.

import { create } from '@tegmentum/ducklink'

const db = await create({ coreUrl: '/ducklink_core.wasm' })
const conn = await db.connect()

const r = await conn.query('SELECT 42 AS answer, $1 AS who', ['world'])
r.toArray()          // [{ answer: 42n, who: 'world' }]   typed JS objects
r.columns            // [{ name:'answer', type:'int64' }, { name:'who', type:'text' }]

// conformance-gated extension load (resolver shim over registry/index.json)
await conn.load('sample_extension')
conn.providerOf('sample_extension')  // 'wasm-component'
await conn.query('SELECT sample_plus_one(41) AS v')   // [{ v: 42n }]

// stream a large result (AsyncIterable over the WIT result-stream resource)
for await (const row of conn.queryStream('SELECT i FROM range(1e6) t(i)')) { /* ... */ }

// register an in-memory file so FROM 'name.csv' resolves
conn.registerFileText('people.csv', 'id,name\n1,ada\n')
await conn.query("SELECT count(*) FROM read_csv('people.csv', header=true)")

Surface

| | | | --- | --- | | create(opts) | instantiate the DuckDB core (coreUrl/coreBytes, registryUrl/registry, artifactUrl, network) | | db.connect(path?) | open a connection (in-memory by default) | | conn.query(sql, params?) | buffered Result; params are positional bind values | | conn.prepare(sql) | PreparedStatement (parameterCount, execute) | | conn.queryStream(sql, {batchSize}) | AsyncIterable<Row> over the result-stream resource | | conn.queryArrow(sql) | Apache Arrow Table (lazy apache-arrow import) | | conn.load(name) / install(name) | resolver-gated extension load; providerOf(name) / loadedExtensions() | | conn.registerFileBuffer/Text/Handle | seed the in-memory FS for FROM 'name' / read_parquet(...) | | conn.insertArrowTable(table, name) / appender(table) | bulk ingest | | Result | .toArray() (typed objects), .toRows(), .get(i), iterator, .columns, .numRows, .toArrow() |

Typed errors: DuckLinkError, QueryError, ExtensionError, ConformanceError, ContractError, InstantiationError, NotFoundError.

Value marshalling (duckvalue <-> JS, BigInt-aware) lives in ONE module (src/values.mjs) so a WIT type-bump is a one-file change.

The resolver gate

conn.load(name) resolves over registry/index.json: it picks the wasm provider whose conformance.passed === true and conformance.at === entry.wit_contract (THE GATE -> ConformanceError / ContractError otherwise), verifies the content_digest of the fetched bytes, then preloads + LOADs it. Each loaded extension gets its own handle namespace (the multi-extension router in @tegmentum/datalink-browser), so load('a') + load('b') coexist.

Verify (headless Chromium)

npm install
# stage the gitignored test/demo artifacts: the @2.2.0 wasip2 core + a
# representative subset of @2.2.0 catalog extensions + the registry index.
# (the full catalog is R2-served; only the bundled subset loads live):
DUCKLINK_DIR=<ducklink> DUCKDB_WASM_DIR=<duckdb-wasm> scripts/stage-public-artifacts.sh
npm run verify        # vite + playwright Chromium (JSPI: Chrome 137+)
npm run test-values   # pure marshalling unit test (Node)

Browser console (try-it-now playground)

An interactive, install-free SQL playground built on this facade (a shell.duckdb.org-style REPL): web/console/.

npm run console:dev      # vite dev server (drives the facade source live)
npm run console:build    # static build -> web/dist (gh-pages-able, base: './')
npm run verify-console   # vite + playwright Chromium end-to-end (captures a PNG)
  • Terminal UI (web/console/repl.mjs): a dependency-free custom REPL — multi-line SQL (Enter submits a ;-terminated statement, Shift+Enter newlines), history, dot-commands. No xterm dependency.
  • duckbox renderer (web/console/duckbox.mjs): renders a Result as the DuckDB-shell Unicode box table — header + dim type row, right-aligned numerics, BigInt/date/decimal formatting, a N rows, M columns footer.
  • Wiring (web/console/console.mjs): conn.query for normal results; results over ~50k rows re-run through conn.queryStream as a chunked, capped preview. Typed errors (QueryError/ConformanceError/NotFoundError) render as clean colored lines.
  • Extension showcase: .load sample_extension resolves through the conformance gate, then SELECT sample_plus_one(41) dispatches into it. The .catalog command lists all registry extensions; the full catalog lights up automatically once the stale @2.0.0 artifacts are reconciled to the @2.2.0 core (ducklink #138) — no console change needed.

The static build copies the wasm core + sample_extension.wasm + registry into web/dist/; apache-arrow stays a lazy chunk (never loaded by the console).

Extension marketplace (browse / inspect / install)

A visual extension marketplace — the browser counterpart to the CLI ext-UX — two-paned with the console so an extension installed on the left is immediately usable in the REPL on the right (one shared connection): web/app/.

npm run app:dev          # vite dev server
npm run app:build        # static build -> web/dist-app (gh-pages-able, base: './')
npm run verify-marketplace  # vite + playwright Chromium end-to-end (captures PNGs)
  • Browse (web/app/marketplace.mjs): a live grid of the 182 catalog extensions — name, one-line description, category chips, provider-kind badges (wasm/native), and a certified/uncertified conformance badge. A search box (name/description/exports/keywords) + a category filter narrow it live.
  • Detail view: the visual equivalent of ducklink ext info — exports (function names), categories, the contract digest + version, and every provider with kind / reference / platform / digest / conformance status. The Resolver verdict panel reuses selectProvider from @tegmentum/datalink-browser to show which provider the resolver chooses and why (or why it is rejected) — never drifting from conn.load().
  • Install / Load: a button calls conn.load(name) (the shared connection); the conformance gate surfaces as a loading -> loaded state or a friendly typed rejection (ConformanceError / ContractError / NotFoundError / InstantiationError). A Loaded bar tracks the current extensions.
  • Console integration: install sample_extension in the marketplace, then SELECT sample_plus_one(41) returns 42 in the console pane — same conn.

Static build copies the wasm core + sample_extension.wasm + registry into web/dist-app/; apache-arrow stays a lazy chunk. The marketplace reuses the JS API + resolver as-is and the console's repl.mjs / duckbox.mjs modules.

Deploy the demo (GCP / Firebase Hosting)

The demo fetches the extension catalog + artifacts directly from the live R2 distribution — the host (GCP/Firebase) is never in the artifact byte path. The distribution base is a single knob, CATALOG_BASE in web/config.mjs:

  • catalog: <base>/ducklink/catalog.json
  • artifacts: <base>/wasm/sha256/<digest>/<name>.wasm (content-addressed, immutable)
  • default <base> = https://datalink-ext.tegmentum.ai; override with the build env VITE_CATALOG_BASE=… or the runtime global window.__CATALOG_BASE.

If the R2 fetch fails (offline / CI / before CORS is applied for the deployed origin), loadCatalog() falls back to the bundled local catalog so the demo still runs — the wasm core is always served locally.

Public layout (scripts/build-demo.sh -> web/dist-deploy/, the Firebase public dir):

  • / — the marketplace + console app (the rich two-pane demo)
  • /console/ — the standalone SQL console
npm run demo:build       # builds both -> web/dist-deploy/ (app at root, console at /console/)

# choose/create a Firebase project, then point .firebaserc at it (replace {{PROJECT_ID}})
firebase deploy --only hosting --project <PROJECT_ID>

firebase.json serves *.wasm as application/wasm, gives hashed assets/** a 1-year immutable cache, and index.html no-cache; apache-arrow stays a lazy chunk. To go live the deployed origin must be allowed by R2 CORS (ducklink deploy/r2/apply-cors.sh, run once the origin is known) — until then the demo runs on the bundled fallback.

Status

Core facade MVP (design steps 1-4) — proven in headless Chromium. Deferred: the worker harness + JSPI fallback (step 5) and the full datalink/browser polish (step 6). JSPI is required (Chrome 137+); create() throws InstantiationError with a clear message where it is unavailable (the worker fallback is the follow-on).

Note: the registry's currently-gated wasm artifacts (aba, ascii85, ...) were built against duckdb:[email protected] while the shipped core is @2.2.0; their stale logicaltype shape is rejected by the core on LOAD. The verify uses sample_extension, which is built against the current contract. This is a ducklink artifact-staleness issue, not a facade bug — the resolver loads exactly what the registry points at.