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

@seekite/engine-native

v0.1.0-alpha.0

Published

Native (napi-rs) build of Seekite's ternlight embedding engine

Readme

@seekite/engine-native

Native (napi-rs) build of Seekite's ternlight embedding engine.

The inference engine and packed weights are derived from Ternlight v0.1.1 (upstream commit c6d2c0a35d14c574ed2898b3dbf95977bca07208) under MIT. The upstream notice ships in @seekite/embeddings-ternlight as TERNLIGHT-LICENSE; model training and artifact provenance are documented in the public provider guide.

@seekite/embeddings-ternlight ships a WebAssembly engine that runs everywhere, including the browser. In Node — CI builds, seekite build, the embedding lab — that leaves throughput on the table. This package exposes the same Rust crate as a native addon, with prebuilt binaries for mainstream platforms and no compile step at install time.

import { createNativeEngine, isSupported } from "@seekite/engine-native"

if (isSupported()) {
  const engine = createNativeEngine()          // "mini" (default) or "base"
  const one = engine.embed("hello world")      // Float32Array(384), L2-normalised
  const many = await engine.embedBatch(chunks) // off-thread, all cores
}

isSupported() returning false is not an error — it means this platform has no prebuild, and the caller should use the wasm engine instead.

The parity contract

Both backends are published under one provider id (seekite:ternlight:mini:v1), so an index built by the native addon has to be queryable by the wasm engine in a browser. That only holds if the two agree exactly, and the test suite asserts byte equality of the output buffers — not approximate equality — over a fixture set covering multilingual text, emoji and ZWJ sequences, combining marks, empty-ish inputs, and inputs past the 128-token truncation point.

This works because the two builds are the same Rust source over the same packed weights. The only divergent code is the BitLinear inner loop (simd128 on wasm; AVX2/SSE4.1 on x86_64, NEON on aarch64), and that loop is an exact i8 × i8 → i32 multiply-accumulate with a proven bound of 128 × 1024, so it cannot overflow and lane ordering cannot change the result.

Batch results carry the same guarantee: embedBatch parallelises across texts, never inside one, so element i is bit-identical to embed(texts[i]) at any thread count.

Throughput

1 000 short chunks, mini model, 16-core x86_64 (AVX2), release build:

| | time | vs wasm | |---|---|---| | wasm sequential | 5.01 s | 1.0× | | native sequential | 2.91 s | 1.7× | | native embedBatch (1 thread) | 2.85 s | 1.8× | | native embedBatch (all cores) | 0.32 s | 15.5× |

The single-threaded gain is the SIMD kernel; the rest is parallelism. Reproduce with pnpm vitest bench packages/engine-native.

Model weights

The native addon carries no embedded weights — it is built with the engine crate's embedded_model feature off, and Engine takes the packed bytes at construction. The two .bin files therefore ship once per package rather than once per platform binary.

They are not stored in git twice: the only committed copy lives inside the wasm artifacts, so pnpm extract-models recovers them from there. That script instantiates the wasm, scans linear memory for the wire format's TERN magic, finds the end via the format's trailing SHA256 self-check, and cross-checks the result against the layout walk in the engine crate before writing anything.

pnpm extract-models   # -> models/mini-int4.bin, models/base-int4.bin

Building locally

pnpm extract-models   # once — the build needs models/ to run the tests
pnpm build            # napi build --release + tsc
pnpm build:debug      # faster compile, ~3× slower embeddings

Prebuilds are produced by .github/workflows/native.yml for linux-x64-gnu, linux-arm64-gnu, linux-x64-musl, darwin-x64, darwin-arm64 and win32-x64-msvc. Anything else falls back to wasm — there is no compile-on-install path, because install has to stay deterministic.

Escape hatch

SEEKITE_DISABLE_NATIVE=1 forces isSupported() to false even when a prebuild is present, so the wasm path can be reproduced without uninstalling anything. CI uses it to prove the fallback still works.

API

function isSupported(): boolean
function nativeBindingError(): Error | undefined
function createNativeEngine(options?: { model?: "mini" | "base" }): {
  readonly dimensions: number
  embed(text: string): Float32Array
  embedBatch(texts: string[], concurrency?: number): Promise<Float32Array[]>
  configSummary(): string
}
function xxhash64(input: string | Uint8Array, seed?: bigint): bigint

xxhash64 is here because spec 06's incremental-build cache keys every source file on every build; doing it in the addon keeps that off the critical path and avoids a second hashing implementation. It returns a bigint — the top bits of a 64-bit hash do not survive a JS number.