@julieisbaka/graphjs
v0.5.1
Published
A zero-dependency, lightweight JavaScript graphing core with powerful plugin hooks.
Maintainers
Readme
GraphJS
GraphJS is a zero-dependency, lightweight JavaScript graphing core built for plugin-first extension.
See CHANGELOG.md for release history.
Documentation
Comprehensive documentation now lives in documentation/docs/.
- Documentation landing:
documentation/README.md - Hub:
documentation/docs/README.md - Installation:
documentation/docs/getting-started/installation.md - Package usage:
documentation/docs/getting-started/package-usage-guide.md - Plugin development:
documentation/docs/guides/plugin-development-guide.md - Troubleshooting:
documentation/docs/guides/troubleshooting.md - Core API reference:
documentation/docs/reference/core-api.md - Hook reference:
documentation/docs/reference/hooks-reference.md - Utilities API reference:
documentation/docs/reference/utils-api.md - Validation API reference:
documentation/docs/reference/validation-reference.md - Default options reference:
documentation/docs/reference/default-options.md - Default options JSON Schema:
documentation/docs/reference/default-options.schema.json - First-party plugin docs:
documentation/docs/plugins/
Each first-party plugin page includes an internal evolution timeline derived from that plugin package's own changelog (extensions/*/CHANGELOG.md).
Goals
- Tiny core, no external runtime dependencies
- Clean graph lifecycle and rendering pipeline
- Comprehensive plugin hooks for first-party and third-party extensions
- Simple line-series baseline to build on
Install
Install from npm:
npm install @julieisbaka/graphjsDevelopment
- Run the test suite with
npm test. The test script uses a small Node launcher that discoverstest/*.test.jsfiles and forwards them to Node's test runner, avoiding shell glob differences and the Node 22 CI issue wherenode --test testcan be treated like a missing module path. - Run
npm run buildto produce a non-bundled ESMdist/output. GraphJS intentionally does not ship a pre-bundled runtime build so application developers can bundle and optimize the library in their own project pipeline.
Publishing
- The release workflow publishes changed packages from
mainusing npm Trusted Publishing via.github/workflows/release.yml. - The workflow creates release metadata in a JavaScript step, then performs the actual
npm publish --provenance --access publiccalls in a dedicated shell step so npm receives the standard GitHub Actions trusted publishing environment. - The root package metadata includes a
repository.urlthat must exactly matchhttps://github.com/Julieisbaka/GraphJS, so the manifest now uses that plain GitHub URL without agit+prefix or trailing.git. - For a brand-new package on npm, publish it manually once first so the package exists and you can attach npm's GitHub Actions trusted publisher configuration to it.
- If npm rejects a publish attempt after the workflow has already built the tarball, bump the package version before retrying so the next release uses a fresh semver.
- After each package is connected to the
julieisbaka/GraphJSrepository andrelease.ymlworkflow in npm, future version bumps can publish from GitHub Actions without anNPM_TOKENsecret. - When publish fails, the workflow now surfaces explicit troubleshooting guidance for scope bootstrap, trusted publisher setup, and npm permission issues.
Quick Start
import { Graph } from "@julieisbaka/graphjs";
const graph = new Graph("#graph", {
width: 800,
height: 420,
plugins: []
});
graph
.setData([
{
id: "revenue",
type: "line",
color: "#2563eb",
pointRadius: 3,
points: [
{ x: 0, y: 12 },
{ x: 1, y: 16 },
{ x: 2, y: 11 },
{ x: 3, y: 19 }
]
}
])
.render();Plugin System
Plugins can be registered globally or passed per graph instance.
Global plugin registration
import { Graph } from "@julieisbaka/graphjs";
Graph.registerPlugin(myPlugin);
const graph = new Graph("#graph", {
plugins: ["myPlugin"]
});Instance plugin registration
const graph = new Graph("#graph", {
plugins: [
{
plugin: myPlugin,
options: { /* plugin config */ }
}
]
});Plugin shape
const myPlugin = {
id: "myPlugin",
priority: 10,
before: ["someOtherPlugin"],
after: ["basePlugin"],
capabilities: {
hooks: ["beforeRender", "afterRender"],
needsLayout: true,
needsBounds: true
},
defaults: {
enabled: true
},
install(graph, options, api) {
// setup work
// api.getPluginState / api.setState / api.registerHook / api.registerCommand
},
commands: {
// declarative command support (optional)
ping(payload, graph, options, api) {
return { ok: true, payload };
}
},
hooks: {
beforeRender(graph, ctx, options, api) {
// return false to cancel this phase
}
}
};Built-in lifecycle hooks
beforeInit,afterInitonStateChange,onPluginEventbeforeSetData,afterSetDatabeforeLayout,afterLayoutbeforeRender,beforeDrawSeries,afterDrawSeries,afterRenderbeforeResize,afterResizebeforeDestroy,afterDestroy
Any hook can return false to cancel the current stage.
Plugin maturity features
- Dependency-aware plugin ordering via
before/after - Optional capability flags (
hooks,needsLayout,needsBounds,needsData) for optimized hook dispatch - Optional plugin error boundary (
pluginErrorBoundary) — live-reconfigurable viagraph.setOptions({ pluginErrorBoundary: ... }) - Plugin identity hardening: local inline plugins cannot use an id that conflicts with an already-registered global plugin id.
- Plugin id collision checks are a runtime security boundary and are enforced in both development and production builds.
Core API
Instance methods
new Graph(canvasOrSelector, options)graph.setOptions(options)graph.getOptions()graph.setDomain(domain)(pass{}to clear domain override)graph.clearDomain()(deprecated, kept for backwards compatibility)graph.getDomain()graph.setBoundsStrategy(fn)graph.setData(series[])graph.addSeries(series)graph.getSeriesById(seriesId)graph.resize(width, height)graph.render({ force?: boolean })graph.clear()graph.destroy()graph.registerCommand(name, handler, metadata?, pluginId?)graph.unregisterCommand(name)graph.executeCommand(name, payload?)graph.listCommands()
graph.clearDomain() is deprecated and kept for backwards compatibility. Prefer graph.setDomain({}) to clear the active domain override.
Static methods and registries
Graph.registerPlugin(plugin)— add a plugin to the global registryGraph.unregisterPlugin(pluginId)— remove a plugin from the global registryGraph.listPlugins()— list all globally registered pluginsGraph.registerRenderer(type, fn)— register a custom series renderer (e.g."bar","scatter")Graph.unregisterRenderer(type)— unregister a custom series rendererGraph.renderers—Map<string, fn>of all registered renderers
Renderer type keys are normalized with trim() during registration and unregistration.
Graph.registerSampler(name, fn)— register a custom data samplerGraph.unregisterSampler(name)— unregister a custom data samplerGraph.samplers—Map<string, fn>of all registered samplers
Notable core options
immutableInputs(boolean): freeze normalized data for safer consumptiondomain({ xMin, xMax, yMin, yMax } | null): override data-derived boundsseries:{ type, color, lineWidth, pointRadius }— per-graph series defaults applied when a series omits those fieldssampling:{ enabled, maxPoints, method }—methodis the name of any registered sampler (built-in:"stride")scalability:{ dirtyRender, layerCaching, useOffscreenCanvas }pluginErrorBoundary:{ enabled, onError }— can be updated live viagraph.setOptions({ pluginErrorBoundary: ... })
Utility exports
GraphJS exports helpers for use in extensions and custom renderers.
All utility functions are available exclusively via the @julieisbaka/graphjs/utils subpath:
decimatePointsStrideresolveCanvasgetDevicePixelRationormalizeSeriesDatagetDataBoundsmakeLinearScaleinvertLinearScaleclampBoundsapplyDomainOverridefilterVisibleSeriesisPlainObjectdeepMergedeepFreezeclamp
Typed API support
GraphJS ships TypeScript declaration files for the core API, plugin contract,
command system, and options (src/index.d.ts). Utility function types are
declared separately in src/utils.d.ts and are exposed via the @julieisbaka/graphjs/utils
subpath entry.
First-party extensions
First-party extensions live at extensions/ in this workspace and are published separately on npm:
@julieisbaka/graphjs-extension-crosshair@julieisbaka/graphjs-extension-legend@julieisbaka/graphjs-extension-pan-zoom@julieisbaka/graphjs-extension-time-scale@julieisbaka/graphjs-extension-tooltip-cursor@julieisbaka/graphjs-extension-watermark@julieisbaka/graphjs-extension-sampling
Each extension is a standalone package with its own package.json and can be used independently.
