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

apex-commons

v0.8.1

Published

Shared utilities for ApexCharts products (licensing, watermark, motion).

Downloads

355,596

Readme

apex-commons

The shared core behind the ApexCharts product family: licensing, watermarking, theming, internationalisation, spring motion and coordinated filtering.

It exists because these things are not chart-specific. A licence key, a theme token, a text direction and a filter mean the same thing to a chart, a tree, a sankey diagram and a grid, and each of those having its own copy meant four implementations drifting apart. Anything the family needs to agree on lives here, so it is agreed on once.

Most users never install this directly. It arrives as a dependency of the product you are using, and the parts you touch are reached through that product's own API. Install it yourself when you are building against several of them at once, or using the crossfilter engine with no chart present.

Installation

npm install apex-commons

Licensing

LicenseManager is the enforcement path for the whole family, so a key set once here is honoured by every Apex product on the page.

import {LicenseManager} from 'apex-commons';

LicenseManager.setLicense('your-license-key');

Without a valid key a product keeps working and carries a trial watermark. That is deterrence and honest-customer compliance rather than DRM: no feature is ever degraded.

The products this serves are free under the Community terms for organisations under the revenue threshold, and need a Commercial licence otherwise. See https://apexcharts.com/pricing.

| Member | Description | | ------------------------- | ------------------------------------------------------------------------ | | LicenseManager | Static class holding the licence for every Apex product on the page. | | setLicense | Set the key. Call before creating any instance. | | getKey | The key currently in force, or null. | | isLicenseValid | Whether the key in force is valid right now. | | isKeyValid | Whether a given key would be valid, without setting it. | | getLicenseStatus | The full LicenseValidationResult for the key in force. | | validateKey | Validate a given key and return its result, without setting it. | | onChange | Subscribe to licence changes. Returns an unsubscribe function. | | LicenseData | The decoded payload: plan, issue and expiry dates, optional domains. | | LicenseValidationResult | The outcome of a check: whether it is valid, and why not when it is not. | | LicenseChangeListener | The callback shape onChange takes. |

Plan tiers

Whether a licence covers a feature, in one place, so every Apex product answers it the same way.

planAtLeast has one hard boundary and one deliberate softness. Without a valid licence it always returns false. With a valid licence whose plan string is not recognised it returns true and warns once, because the holder has a signed, unexpired key: refusing them a feature over a string we failed to map is worse than granting one. Gating here is deterrence and honest-customer compliance, not DRM.

import {planAtLeast} from 'apex-commons';

if (planAtLeast('premium')) {
  // show the tool
}

| Member | Description | | --------------- | -------------------------------------------------------------------------------------- | | planAtLeast | Whether the licence in force covers a feature needing at least the given plan. | | currentPlan | The plan the licence grants, or null. For display; use planAtLeast to gate. | | normalisePlan | Resolve a raw plan string to a known plan, ignoring case and space. null if unknown. | | PLAN_ORDER | Every plan string a key may carry, in commercial ladder order. Not the gating order. | | PLAN_ALIASES | Plan strings that may appear in issued keys but are not current plan names. | | ApexPlan | The plan names: community, pro, premium, embedded, oem. |

There are three gating levels, not five. community < pro < premium, and embedded and oem rank equal to premium rather than above it. Those two sell redistribution rights and, in the words of the licence they are sold under, not usage rights: what an OEM customer's runtime may do is decided by the per-developer licence bought alongside, which is what the key carries. So premium is the ceiling, and nothing should gate above it. A feature that did would be reachable by no purchasable licence at all.

Both strings stay in the vocabulary rather than being removed, because planAtLeast fails open on a plan it does not recognise: drop them and a key carrying one would be granted everything, which is the opposite of the intent.

community never appears in a key, because that tier is issued none, so planAtLeast('community') means "holds any licence at all".

oem was called enterprise until 2026-09-14, and PLAN_ALIASES maps the old string so a key carrying it still resolves to a real rank rather than fail-opening.

Watermark

The trial mark an unlicensed product adds to its container.

| Member | Description | | ------------------ | ------------------------------------------------------------ | | Watermark | Static class that adds, finds and removes the trial mark. | | WatermarkOptions | Options accepted by add and remove. | | ATTR | The attribute the mark carries, for finding it. | | add | Add the mark to a container. Returns the element, or null. | | remove | Remove the mark from a container. | | exists | Whether a container currently carries a mark. | | node | The mark element inside a container, or null. | | applyStyles | Apply the mark's styles to an element. | | untrack | Stop tracking a container, without removing its mark. |

Internationalisation

| Member | Description | | ----------------- | -------------------------------------------------------------------------- | | TextDirection | 'ltr', 'rtl' or 'auto'. | | resolveMessages | Merge a caller's partial message overrides onto a product's defaults. | | applyDirection | Set text direction on an element, resolving 'auto' against the document. | | isRTL | Whether a direction resolves to right-to-left, given an optional element. |

Theming

A page declares the --apx-* custom properties once on :root and every Apex product on it follows. Anything set explicitly on a product wins, so a product already themed through its own options is untouched.

| Member | Description | | --- | --- | | ApxTokens | The resolved token set: accent, foreground, grid, surface and the series palette. | | APX_TOKEN_VARS | The custom property name for each token role. | | APX_SERIES_VAR_PREFIX | Prefix for the ordered series palette, --apx-series-N. | | readTokens | Read the tokens currently in force on an element. | | resolveTokenLayer | Read the tokens for an element, with a registered theme underneath. | | pickToken | Resolve one value through the precedence order: option, then CSS variable, then token, then fallback. | | TokenPrecedenceInput | The candidates pickToken chooses between. | | ApxTheme | A named theme: the token roles it contributes. | | registerTheme | Register or override a named theme on the shared registry. | | getTheme | Look up a registered theme by name. | | registeredThemes | The names currently registered. | | unregisterTheme | Remove a registered theme. | | OwnedToken | Tracks a custom property a product set itself, so it can put back what it found. | | OSThemeWatcher | Watches the operating system's light and dark preference. | | OSThemeState | The current OS preference. | | OSThemeWatcherOptions | Options for the watcher. |

Motion

A headless spring driver, shared so that motion feels the same across the family. It computes values and holds no opinion about what draws them.

| Member | Description | | ----------------------------- | ----------------------------------------------------------------------------- | | Spring | One spring: its current value, velocity, target and rest thresholds. | | makeSpring | Create a spring at a value, with a stiffness and damping. | | stepSpring | Advance a spring by a timestep. Returns whether it is still moving. | | retarget | Point a spring at a new target, keeping its velocity. | | snapSpring | Move a spring to a value immediately, clearing velocity. | | SpringRest | The velocity and displacement below which a spring counts as arrived. | | UNIT_REST | Rest thresholds for springs running over a 0 to 1 range, rather than pixels. | | SpringPreset | 'crisp', 'gentle' or 'snappy'. | | SPRING_PRESETS | The stiffness and damping pair behind each preset. | | resolveSpring | The pair for a preset, falling back to the default. | | CAMERA_SPRING | The pair used for pan and zoom, which wants less overshoot than content. | | WAVE_STAGGER_MS | Delay between neighbours in a staggered entrance. | | SpringScene | Drives many named springs on one animation loop, with a single rest callback. | | SpringSceneOptions | Options for a scene, including its clock. | | SpringValues | The current value of every spring in a scene, by name. | | SceneObjectOptions | Options for one object added to a scene. | | SceneClock | The scene's time source. Substitutable, so tests need no timers. | | diffNodes | Split previous and next id sets into entering, surviving and leaving. | | NodeDiff | The result of diffNodes. | | ringDistances | Distance of each node from a set of roots, for staggering a wave outwards. | | ReducedMotionWatcher | Watches the user's reduced-motion preference. | | ReducedMotionWatcherOptions | Options for the watcher. |

Crossfilter

Coordinated filtering across any number of views. One shared record set, and per view a dimension (row => key), a reduction and a filter.

A view's aggregation is computed over the records passing every other view's filter, never its own, so each view keeps showing what else is reachable instead of collapsing to its own selection. A view's own filter only marks which of its buckets are selected.

Because a dimension is an arbitrary function, a map region, a tree subtree, a sankey link, a grid column and a chart category are all the same thing to it, and a set of views can coordinate with no chart present at all.

import {Crossfilter} from 'apex-commons';

const cf = Crossfilter.getOrCreate({id: 'audit'});
cf.setRecords(rows);
cf.registerDimension('byRegion', {dimension: (r) => r.region});
cf.on('change', () => render(cf.aggregateAll()));
cf.filter('byRegion', ['EU']);

| Member | Description | | ------------------- | ------------------------------------------------------------------ | | Crossfilter | The coordinator. | | getOrCreate | Static. The coordinator for an id, creating it if needed. | | get | Static. The coordinator for an id, or null. | | setRecords | Replace the shared record set. | | registerDimension | Add or replace a view's dimension and reduction. | | hasDimension | Whether a view is registered. | | removeDimension | Remove a view. | | filter | Set a view's filter. | | toggleKey | Add or remove one key from a view's filter. | | filterOf | A view's current filter, or null. | | clear | Clear one view's filter. | | reset | Clear every filter. | | aggregateFor | The aggregation for one view. | | aggregateAll | The aggregation for every registered view. | | filteredRecords | Records passing every filter, optionally excluding one view's own. | | filteredRows | Records passing every filter. | | state | A snapshot of records, filters and aggregations. | | on | Subscribe to an event. Returns an unsubscribe function. | | off | Remove a handler. | | dataTable | Render a paged table of the filtered rows into an element. |

Crossfilter types

| Type | Description | | --------------------- | ------------------------------------------------------------- | | CrossfilterOptions | Options for getOrCreate, including the shared id. | | CrossfilterRow | One record. Shape is entirely the caller's business. | | CrossfilterState | The snapshot state() returns. | | CrossfilterEvent | The events a coordinator emits. | | CrossfilterListener | The callback shape on takes. | | DimensionSpec | A view's dimension, reduction, ordering and binning. | | DimensionAccessor | Maps a row to this view's key. | | DimensionType | Whether a dimension is categorical or numeric. | | DimensionOrder | How a view's buckets are ordered. | | BinSpec | How a numeric dimension is bucketed. | | ReduceSpec | How rows in a bucket become a scalar. | | Reducer | A custom reduction function. | | FilterInput | What filter accepts: a set of keys, or a numeric range. | | Aggregate | Union of the aggregation shapes. Narrow it before reading. | | CategoryAggregate | A flat list of keys and values. | | RangeAggregate | A numeric range aggregation. | | MatrixAggregate | A two-dimensional aggregation, which has no flat keys list. | | DataTableOptions | Options for dataTable. | | DataTableColumn | One column in a data table. | | DataTableHandle | The handle dataTable returns. |

Indicators

Moving-window indicators as pure functions, lifted out of apexstock so the chart family and the stock product share one implementation. Each takes Array<number | null> and returns the same length, with null wherever a value is not defined.

A window containing a gap yields null rather than an average of however many values were present, which would quietly compute a mean of four and show it beside means of ten.

sma is slice-invariant: a window averages to the same value whatever precedes it, so sma(values.slice(i - period + 1, i + 1), period) at its last position equals sma(values, period)[i] exactly. That is what lets a streaming consumer extend a series by recomputing only the tail window. It costs a running total, so sma is O(n * period).

| Member | Description | | --- | --- | | sma | Trailing simple moving average, aligned to the last position in each window. Slice-invariant, as above. | | ema | Exponential moving average, seeded from the first complete simple window rather than the first value. A gap ends the run and the next complete window starts a new one. | | bollinger | A simple moving average with a moving standard deviation either side, using the population denominator, which is the convention for this band. | | Bands | What bollinger returns: middle, upper and lower, each the length of the input. |

import {bollinger, ema, sma} from '@apex/commons';

sma([1, 2, 3, 4, 5, 6], 3); // [null, null, 2, 3, 4, 5]
ema([1, 2, 3, 4, 5], 3); // [null, null, 2, 3, 4]
bollinger([2, 4, 4], 3, 2).upper;

Histogram binning

Observations in, bin edges and counts out. Pure arithmetic, so it is testable on its own and inert under SSR.

Gaps mean the opposite of what they mean above. A moving average over a window containing a gap is undefined, because the window is a claim about a stretch of the series. A histogram is a tally of the observations that exist, so a gap is simply not one; histogram skips them and reports how many it skipped.

| Member | Description | | --- | --- | | histogram | Bin a series and count it in one call, tolerating gaps. The convenience most callers want. | | computeBinning | Choose bin edges for a set of finite observations. Precedence: an explicit binWidth, then an explicit bin count, then a named rule. | | binCounts | Count observations into a set of edges. | | binIndexOf | The bin holding one value, or -1 outside the edges. Bins are half-open except the last, which includes its upper edge. | | quantileSorted | Quantile of an ascending-sorted array, by the "type 7" definition that R, NumPy and a spreadsheet's QUARTILE all share. | | Histogram | What histogram returns: a Binning plus counts, midpoints, total and missing. | | Binning | edges, binWidth, the rule that actually chose the width, and whether the count was capped. | | BinOptions | bins (a rule name or a fixed count), binWidth, and a range to frame the bins on so several histograms share a scale. | | BinRule | The named rules: auto, fd, rice, scott, sqrt, sturges. auto is the narrower of fd and sturges, falling back to sturges when the IQR is zero. |

import {histogram} from '@apex/commons';

const h = histogram([1, 2, 2, 3, null, 9]);
h.counts; // observations per bin
h.missing; // 1

Browser support

Every module is SSR-safe: nothing touches document at import time, and the pieces that need the DOM take the element they act on. Crossfilter is DOM-free apart from its optional dataTable helper.