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

jena-js

v0.6.2

Published

Standalone TypeScript/JavaScript library for Epistemic Network Analysis, ported from rENA for browser and Node runtimes.

Readme

jENA

CI npm

jena-js is a standalone TypeScript/JavaScript implementation of Epistemic Network Analysis (ENA) for browser and Node runtimes, ported from and verified against the rENA R package (0.3.1).

The runtime is pure JavaScript with zero runtime dependencies. It does not require R, Rserve, OpenCPU, or a server process to run ENA models. R is used only at development time to generate golden fixtures and verify numerical parity with rENA.

Install

npm install jena-js

or the development version straight from main:

npm install github:HUDongpin/jENA

ESM only. The package ships ES modules and requires Node ≥ 18 or a bundler; require("jena-js") is not supported.

Usage

import { ena } from "jena-js";

const set = ena({
  rows: [
    { unit: "u1", conv: "c1", A: 1, B: 0, C: 0 },
    { unit: "u1", conv: "c1", A: 0, B: 1, C: 0 },
    { unit: "u2", conv: "c1", A: 1, B: 1, C: 0 }
  ],
  units: ["unit"],
  conversation: ["conv"],
  codes: ["A", "B", "C"],
  model: "EndPoint",
  window: "MovingStanzaWindow",
  weightBy: "binary",
  windowSizeBack: 2,
  dimensions: 2
});

console.log(set.points);          // projected unit points (SVD1, SVD2)
console.log(set.rotation.nodes);  // code node positions
console.log(set.variance);        // variance explained per rotated dimension

Note on variance: shares are normalized across all rotated dimensions (rENA semantics), so SVD1 + SVD2 generally sums to less than 1. Dimension signs are arbitrary (SVD sign indeterminacy) — compare axes up to sign. See NUMERICS.md.

Entrypoints (API tiers)

The root export is the stable tier — the verified pipeline, types, stats, and accumulation kernels only (~two dozen names, semver-guarded). Everything else lives behind subpaths:

import { ena, accumulateData, makeSet, projectIn, enaCorrelations, cohensD } from "jena-js";
import { rotateByMean, svdRotation, lwsLeastSquaresPositions } from "jena-js/rotation";
import { createENAPlotModel, toPlotly, renderENAPlot } from "jena-js/plot";
import { createENAWorkerClient } from "jena-js/browser";       // worker bundle: "jena-js/browser/worker"
import { multiGaussianElasticNet } from "jena-js/experimental"; // may change in any release
import { solveLinearSystem } from "jena-js/core";               // internal tier: no semver guarantees

RotationOptions is a discriminated union — each rotation method's parameter shape is enforced at compile time.

Migrating from ≤0.5.x: the root previously re-exported the entire codebase. Rotation functions moved to jena-js/rotation, plot helpers to jena-js/plot, the worker client to jena-js/browser, the elastic net and typed-array table helpers to jena-js/experimental, and numerical internals (linear solvers, Gram-Schmidt, matrix utilities) are only available from jena-js/core. Code that used ena/accumulateData/makeSet/projectIn/stats from the root is unaffected.

Verified vs experimental surface

Everything in the verified tier is tested against golden outputs generated from rENA 0.3.1 (see fixtures/goldens/, version-stamped; tolerances documented in NUMERICS.md).

| Area | Status | Notes | |---|---|---| | Accumulation: EndPoint, AccumulatedTrajectory, SeparateTrajectory | ✅ Verified | All 16 golden configs, both fixture datasets | | Windows: moving stanza (back/forward/infinite), conversation | ✅ Verified | Line-faithful port of rENA's C++ ref_window_df | | Weighting: binary, sum, custom functions | ✅ Verified | Custom functions golden-tested (sqrt configs); applied once per windowed co-occurrence cell before unit accumulation — matches rENA's implementation (its docstring says otherwise; noted in rENA#48) | | Sphere normalization, centering | ✅ Verified | Includes rENA's zero-row semantics | | SVD rotation, means rotation | ✅ Verified | Points at ~1e-9, rotation matrices up to sign | | Variance explained | ✅ Verified | Normalized over all dimensions, 1e-9 vs rENA | | Undirected node positions + centroids | ✅ Verified | See NUMERICS.md for singular-system tolerance | | projectIn (rotation-set reuse) | ✅ Verified | Self-projection invariant + shared machinery | | Stats: enaCorrelations, cohensD | ✅ Verified | vs rENA::ena.correlations / fun_cohens.d | | Stats: Welch t, one-way ANOVA (enaStats) | ✅ Verified | vs R t.test / aov; no p-values — statistic + df only | | Rotations: regression, regression2 | ✅ Verified | vs ena.rotate.by.hena.regression/_2 — x-only, x+y, and multi-term formulas (12 golden configs) | | Rotation: generalized (single covariate) | ✅ Verified | vs ena.rotate.by.generalized incl. select2Groups; variance compared junk-aware (see NUMERICS.md) | | Rotation: hena | ✅ Verified | vs ena.rotation.h — x/y, factor-expanded controls, interaction, centering on/off (6 golden configs), incl. rENA's run-length dummy coding | | Elastic-net solver (multiGaussianElasticNet) | ✅ Verified | vs glmnet mgaussian at fixed lambdas (1e-6): group lasso, standardization, penalty factors, α-mixing | | Streaming/chunked accumulation | ✅ Verified | Single engine since 0.6.0 (batch delegates to it): all rENA golden parity suites run through it, plus chunk-size equivalence and a 120-case randomized property suite with an independent window oracle | | Rotation: generalized with multiple covariates | ⚠️ Solver-verified | The elastic-net solver matches glmnet at equal lambda (1e-6). Full-pipeline parity is out of reach for two reasons: rENA selects lambda via cv.glmnet, whose fold assignment is randomized (irreproducible across runs unless the caller seeds R's RNG — rENA does not), and rENA's design matrix (model.matrix(~ .^2): factor expansion, all pairwise interactions) differs from jena's simplified design. jena is deterministic end-to-end (round-robin folds over a glmnet-style path) — see NUMERICS.md | | Rotation: spherical | 🔷 jena extension | No rENA counterpart exists — anchors axes at chosen adjacency directions; spec-tested (orthonormality, anchor semantics) | | Directed node positions | 🧪 Experimental | Throws on this pipeline's undirected models; only usable with external n×n directed adjacency data | | Plot model, plotly adapter, SVG renderer | ✅ Tested (jena-specific) | 16 contract tests pin every builder and the plotly trace/layout mapping; SVG renderer smoke-tested in real Chromium. No rENA golden — rENA's R-plotly stack is structurally incomparable | | Worker client + protocol | ✅ Protocol + browser tested | Versioned protocol v1: chunked progress, cooperative cancel, crash/timeout/abort rejection — 12 protocol tests on an in-memory channel plus real-Chromium Worker round-trip tests (result parity, progress, mid-flight cancel) |

If a rotation or statistic will end up in a publication, stay on the ✅ tier or independently validate your configuration against rENA first.

Input validation

Malformed inputs throw descriptive errors instead of producing quietly wrong numbers: empty rows, fewer than 2 codes, negative/fractional window sizes, wrong-shape masks, misspelled model/window/weightBy/rotation.method/nodePositionMethod values, dimensions < 1, and unitsUsed filters that match nothing are all rejected.

Development

npm install
npm run lint
npm run typecheck
npm test
npm run build
npm run pack:check

Golden fixtures are committed, so tests run without R. To regenerate them (requires R with rENA installed):

npm run goldens:r       # model fixtures -> sena-configs.regenerated.json
npm run goldens:diff    # compare regenerated vs committed at 1e-9
npm run goldens:stats   # stats fixtures (correlations, cohen's d, t/F tests)

See fixtures/goldens/README.md for the regenerate → diff → adopt workflow. CI runs lint, typecheck, tests, build, a packaging check, and a packed-tarball consumer smoke test on Node 18/20/22.

Provenance and license

jena-js is a TypeScript translation of core rENA behavior — rENA 0.3.1 (GPL-3) by Cody L Marquart, Zachari Swiecki, Wesley Collier, Brendan Eagan, Roman Woodward, and David Williamson Shaffer. This package is distributed under GPL-3.0-only, preserving the upstream license. See PROVENANCE.md for the upstream version pin, source history, and file-level attribution, and NUMERICS.md for documented numerical deviations and agreement bounds.