Premium and licensing
Premium chart families and separate type modules, licence keys, domain binding, watermark and dev hosts.
west-coast-charts/premium adds nine chart types for operational and analytical dashboards:
chart.type |
Use it for |
|---|---|
sankey |
flows between stages (traffic, budgets, funnels with branches) |
chord |
two-way relationships between groups (service-to-service calls, trade) |
gantt |
schedules with dependencies, baselines and calendars |
tree |
hierarchies and org charts with cards and semantic zoom |
stock |
financial time series with indicators and drawing tools |
map |
choropleths, markers and routes (world and US states maps included) |
marimekko |
two-dimensional share (market × segment) |
network |
node-link graphs |
radialColumn |
radial dashboards that combine several rings with linked highlighting, see the radial column guide |
Two more premium types come as a separate, lazily loaded type module,
west-coast-charts/layers/quint (WestCoastCharts.use([premium, quint])):
chart.type |
Use it for |
|---|---|
quintTimeline |
an operations day on one time axis: requests and error rate, cost heatmaps, error budgets, latency lines, region traffic, incidents and deploys, see the Quint guide |
quintConvergence |
the same lanes with a labelled focus lens that magnifies the time around an incident (time only, never values) |
Two further families are supplied as optional modules: west-coast-charts/layers/vitals
adds vitalsClock (Vitals Clock), and west-coast-charts/layers/schedule adds
schedule (Practice Schedule). The complete Premium offer therefore contains
thirteen families: nine in the main bundle and four in the type modules.
The paid offer covers the separately supplied Premium distribution and services described in the business terms. It does not replace the MIT licence on MIT-marked code. See the licensing overview for the scope and links to the original notices and full terms.
Premium code is supplied in the separate complete package obtained through a trial or paid licence. The public MIT Core package does not export /premium, /layers or /maps. Install the complete package instead of the Core-only package before using the examples below. Without a licence key every chart renders and works fully, and on public host names it shows a small "unlicensed" line. Development hosts never show it.
- Loading premium
- Licence keys
- Domain binding
- The unlicensed watermark
- Development and test hosts
- Browser requirements (ES2020)
- FAQ
Loading premium
With a bundler, install the plugin once at startup:
import WestCoastCharts from 'west-coast-charts'
import premium from 'west-coast-charts/premium'
WestCoastCharts.use(premium)
const chart = new WestCoastCharts(document.querySelector('#chart'), {
chart: { type: 'sankey', height: 320 },
series: [{
data: [
{ from: 'Visits', to: 'Pricing page', value: 4200 },
{ from: 'Visits', to: 'Docs', value: 2600 },
{ from: 'Pricing page', to: 'Trial', value: 1100 },
{ from: 'Docs', to: 'Trial', value: 700 },
{ from: 'Trial', to: 'Paid', value: 420 },
],
}],
})
await chart.render()With script tags, load the premium bundle after the core. It installs itself into the global
WestCoastCharts; its own global is WestCoastChartsPremium.
<script src="./dist/west-coast-charts.min.js"></script>
<script src="./dist/west-coast-charts-premium.min.js"></script>
<div id="chart"></div>
<script>
WestCoastCharts.setLicenseKey('YOUR-LICENCE-KEY') // see below; omit while evaluating
new WestCoastCharts(document.querySelector('#chart'), {
chart: { type: 'chord', height: 360 },
series: [{
data: [
{ from: 'web', to: 'api', value: 120 },
{ from: 'api', to: 'db', value: 80 },
{ from: 'api', to: 'cache', value: 60 },
{ from: 'worker', to: 'db', value: 40 },
],
}],
}).render()
</script>Install the plugin before the first premium chart renders. Without it, render() rejects with a message
that tells you to import and use() the premium plugin.
The map type needs map shapes. world and us-states ship in west-coast-charts/maps/*
(dist/maps/*.js for script tags) and register themselves with WestCoastCharts.registerMap.
Licence keys
A licence key is a single string you receive from West Coast Software when you buy a licence. It is checked offline in the browser: there is no network request, no telemetry and no licence server.
Register it once, before you create premium charts:
import WestCoastCharts from 'west-coast-charts'
import premium from 'west-coast-charts/premium'
WestCoastCharts.use(premium)
const licence = await WestCoastCharts.setLicenseKey('YOUR-LICENCE-KEY')
if (!licence) {
// invalid, expired, for another major version or another domain: charts still work, with the watermark
console.warn('WestCoast Charts licence not accepted')
}setLicenseKey(key) returns a promise. It resolves to the key's contents when the key is valid for this
page, and to null otherwise. It never throws and never blocks rendering. The contents look like this:
| Field | Meaning |
|---|---|
l |
licensee (your company) |
t |
tier: 'premium' or 'oem' |
e |
expiry in milliseconds since 1970; 0 = perpetual |
v |
[first, last] major versions covered, inclusive |
i |
issue date (milliseconds since 1970), optional |
d |
host patterns the key is bound to, optional (see Domain binding) |
The same function is available as premium.setLicenseKey(key). To use a different key for one chart,
for example in a multi-tenant page, set chart.licenseKey; it wins over the global key for that chart:
import WestCoastCharts from 'west-coast-charts'
import premium from 'west-coast-charts/premium'
WestCoastCharts.use(premium)
const chart = new WestCoastCharts(document.querySelector('#chart'), {
chart: { type: 'marimekko', height: 300, licenseKey: 'TENANT-SPECIFIC-KEY' },
series: [
{ name: 'Enterprise', data: [{ x: 'EMEA', y: 40 }, { x: 'Americas', y: 55 }, { x: 'APAC', y: 25 }] },
{ name: 'SMB', data: [{ x: 'EMEA', y: 30 }, { x: 'Americas', y: 20 }, { x: 'APAC', y: 35 }] },
],
})
await chart.render()
// 'TENANT-SPECIFIC-KEY' is a placeholder, so this example shows the watermarkHow a key is checked:
- The signature is verified with ECDSA P-256 / SHA-256 in plain JavaScript. It takes roughly 15 ms once per key and page, and the result is cached.
- It needs no
crypto.subtle, so it works the same on HTTPS, plain HTTP, intranet host names and bare IP addresses. - A key covers the major versions in
v. The version that counts is the major number of the installed package: WestCoast Charts 1.0.0 and every later 1.x release are major 1, so a key withv: [1, 1]covers all of them. - The runtime must also trust the key's signer. Version 1.1.1-rc.4 introduces a new active signer and retains the previous public key for existing licences. Keys issued by the new signer need 1.1.1-rc.4 or a later runtime that trusts it; older runtimes do not accept those new signatures merely because the key's major-version range includes 1. Existing licences retain their original expiry, major-version and host restrictions.
- An expired key is treated like a missing key: the watermark returns, nothing stops working.
Issuing evaluation, review and gift keys
The companion tools/license/issue-access.cjs supports --purpose evaluation
and --purpose review, delegating signing to the unchanged approved issuer. These future
keys expire 30 days after issuance by default; an explicit --expires must be
after issuance. They cannot use --perpetual or an expiry of zero. Add
--domains "review.example.com" when the evaluation host is known; host binding
is optional when reviewers need several unknown environments.
Intentional gifts use explicit --purpose grant; the additional --gift flag
is rejected without that purpose. A grant may deliberately be perpetual. The
ten previously issued perpetual gift keys retain their original major-version,
host and expiry rights. They are not reclassified as trials or modified by this
issuance policy. Existing paid issuance remains backward compatible: omitted
--purpose means paid, with its previous expiry behavior. Purpose controls
issuance only and is not a new runtime permission or signed payload field.
node tools/license/issue-access.cjs --licensee "Review organisation" --purpose review --domains "review.example.com"
node tools/license/issue-access.cjs --licensee "Intentional gift" --purpose grant --gift --perpetual --versions 1Verification is offline. There is no online revocation service or global revocation switch. Changing future issuance defaults does not revoke an issued signature. An older installed bundle continues accepting a signature it trusts while that key satisfies the bundle's existing expiry, version and host checks. A future runtime could change its own trust policy, but that would not update older copies or withdraw independently granted licence rights.
Domain binding
A key may carry host patterns in d. The key is then accepted only when the page's host name matches
one pattern completely (case-insensitive, port ignored). * stands for any run of characters,
including dots.
| Pattern | Matches | Does not match |
|---|---|---|
acme.com |
acme.com |
www.acme.com |
*.acme.com |
app.acme.com, eu.app.acme.com |
acme.com |
10.20.* |
10.20.0.5, 10.20.31.7 |
10.2.0.5 |
ops-* |
ops-grafana, ops-01 |
devops-01 |
A key without d is valid on every host. To cover both the apex domain and its subdomains, the key
needs both patterns: acme.com and *.acme.com.
The unlicensed watermark
When a premium chart renders on a host that is not exempt (below) and no valid key applies, the chart gets one quiet line in its bottom-right corner:
WestCoast Charts Premium · unlicensed
- It is SVG text (
<text class="wcc-unlicensed">) in 10 px, the theme's muted colour at reduced opacity,pointer-events: noneandaria-hidden="true", so it never blocks interaction or reaches screen readers. - It is part of the chart SVG, so it also appears in PNG and SVG exports.
- The console shows one warning per page:
WestCoast Charts premium: no valid licence key for this site, see WestCoastCharts.setLicenseKey(). - Nothing else changes: no feature is disabled, no data is hidden, nothing is sent anywhere.
- Core and
west-coast-charts/typescharts never get a watermark.
import WestCoastCharts from 'west-coast-charts'
import premium from 'west-coast-charts/premium'
WestCoastCharts.use(premium)
// a public host without a key: the chart works and carries the watermark
await new WestCoastCharts(document.querySelector('#chart'), {
chart: { type: 'network', height: 300 },
series: [{
data: [
['gateway', 'auth'],
['gateway', 'orders'],
['orders', 'payments'],
],
}],
}).render()Set the key before the first premium chart renders. A watermark that is already drawn stays until the chart redraws.
Development and test hosts
These pages never show the watermark, with or without a key:
localhost,127.0.0.1and[::1]- host names ending in
.localor.localhost(for examplemyapp.localhost,printer.local) file://pages- environments without
locationor without a host name (server-side code, workers, jsdom without a URL)
import WestCoastCharts from 'west-coast-charts'
import premium from 'west-coast-charts/premium'
WestCoastCharts.use(premium)
// on a dev server: no key needed, no watermark
await new WestCoastCharts(document.querySelector('#chart'), {
chart: { type: 'sankey', height: 260 },
series: [{ data: [{ from: 'A', to: 'B', value: 3 }, { from: 'A', to: 'C', value: 2 }] }],
}).render()Staging and preview hosts (staging.acme.com, *.vercel.app, an intranet IP) are not exempt; include
them in the key's patterns.
setLicenseKey() itself has no exemption: on localhost it still tells you whether the key would be
accepted, so you can test a key before you deploy it.
import WestCoastCharts from 'west-coast-charts'
import premium from 'west-coast-charts/premium'
WestCoastCharts.use(premium)
const result = await WestCoastCharts.setLicenseKey('not-a-real-key')
console.log(result) // null: the dev host hides the watermark, but the key itself is rejected
if (result !== null) throw new Error('expected null')Browser requirements (ES2020)
The premium bundle is built for ES2020 engines because the offline signature check uses BigInt:
| Browser | Minimum |
|---|---|
| Chrome, Edge | 80 |
| Firefox | 80 |
| Safari (macOS, iOS) | 14 |
Older engines (Internet Explorer, legacy Edge, Safari 13) cannot load the premium bundle; the core and the chart types still run there (ES2018, see Getting started). The ES2020 build also keeps the bundle smaller: it saves 2,232 B gzip, more than the 1,708 B gzip the licence check adds (figures from docs/architecture/size-ledger.md (library repository)).
FAQ
Does the licence check contact a server? No. The key carries its own signature, which is checked against a public key built into the bundle.
Which releases does my key cover? Signature acceptance requires both a trusted signer and the major-version range in the key. The commercial offer covers major version 1 and available updates during the paid term. Later major versions are not included unless separately agreed. New RC4-signer keys require RC4 or a later runtime trusting that signer; older RC3 runtimes do not accept them.
What happens when a key expires? An expired technical key brings back the watermark and console warning; charts keep working. A paid annual entitlement and a key's technical expiry are different: a key may remain valid to support continued use of an existing licensed deployment after the paid term ends. This does not extend update delivery or support.
What does the watermark mean? It indicates that the runtime has not accepted a key for this chart and host. It does not determine or limit independent MIT rights. Contact service@pekagi.io for help with a purchased key or an integration issue.
What licence applies to each part? The MIT-marked source keeps its MIT licence. Premium distribution, updates and support have separate business terms. See the licensing overview, package LICENSE, and NOTICE.
Network layouts and timing diagnostics
The default plotOptions.network.layout.type: 'force' computes positions. If your
application already has a layout, choose 'preset' and provide finite numeric
x and y on every item in series[].nodes:
import WestCoastCharts from 'west-coast-charts'
import premium from 'west-coast-charts/premium'
WestCoastCharts.use(premium)
const chart = new WestCoastCharts(document.querySelector('#chart'), {
chart: { type: 'network', height: 400 },
series: [{
nodes: [{ id: 'api', x: -60, y: 0 }, { id: 'db', x: 60, y: 0 }],
edges: [{ source: 'api', target: 'db', weight: 3 }],
}],
plotOptions: { network: { layout: { type: 'preset' } } },
})
await chart.render()
const timings = premium.api(chart).performanceStats()Preset coordinates are world coordinates, fitted to the chart viewport without running a force simulation or moving overlapping nodes apart. Nodes referenced by edges also need explicit positioned definitions. Missing or invalid positions produce a one-time warning naming the nodes and fall back to the force layout. Dragging changes the current view; a subsequent data/options update reads the supplied positions again. Preset mode is independent of SVG/Canvas rendering.
Canvas changes drawing, not the force-layout calculation. Similar total SVG and
Canvas render times can occur when layout dominates. Compare the same data,
options, browser and hardware before drawing a performance conclusion.
performanceStats() returns a diagnostic copy containing modelMs, layoutMs,
drawMs, labelsMs, renderMs, frames, settled, the actual layout, and
renderer. It returns null for a chart without network data.
The instrumented phases accumulate for the current renderer instance. modelMs
covers model construction, layoutMs layout setup and simulation/decluttering,
drawMs drawing setup and frames, and labelsMs label planning. Updating label
anchors during a frame belongs to drawing. Phase counters are scoped diagnostic
regions rather than an exhaustive partition of all application work; their sum
is not a whole-page load time. renderMs measures the initial renderer call.
With animated force layouts the first render is labelled while geometry can
continue moving; listen for layoutSettled if you need the settled arrangement.
Timings depend on the environment and are excluded from describe() and the
semantic data contract.
WestCoast Charts 1.1.5 · generated from the library guide at commit 95f093ba. Code examples in the guide are executed by the library's docs check.