npm.io
0.8.1 • Published 2d ago

apex-commons

Licence
SEE LICENSE IN LICENSE
Version
0.8.1
Deps
0
Size
175 kB
Vulns
0
Weekly
0

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.