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

@accurix/centrinfo-analytics

v3.0.0

Published

Secure, framework-agnostic SDK for embedding Centrinfo Answers and Liveboards.

Readme

@accurix/centrinfo-analytics

Embed polished Centrinfo Liveboards and Answers in a customer application without exposing a Centrinfo user session, semantic-model details, SQL, or a signing key to the browser.

This package is intentionally separate from @accurix/centrinfo-widget:

  • Use @accurix/centrinfo-widget for the floating or full-page conversational chat experience.
  • Use @accurix/centrinfo-analytics for Liveboards and standalone Answers.
  • Both surfaces consume the same governed analytics result contract, so KPIs, charts, explanations, filters, and customer permissions remain consistent.

What the embed includes

  • Responsive 12-column Liveboard layouts that stack cleanly on mobile.
  • Optional dashboard pages and nested tabs, rendered from the same published Liveboard definition used inside Centrinfo.
  • KPI, line, multi-line, bar, pie, scatter, funnel, and table results.
  • Chart/table switching, chart sorting, always-visible chart zoom controls, card expansion/minimization, fullscreen, and refresh.
  • Plain-language explanations that separate facts, possible drivers, directional outlooks, and recommended next steps.
  • Approved filter presets, reporting periods, freshness, and safe row counts.
  • A built-in governed dashboard assistant for plain-language follow-up questions and multi-answer comparisons, even when the public chat widget is not installed.
  • Optional CSV/chart downloads plus polished whole-dashboard PDF and PowerPoint exports.
  • Per-card failures, loading skeletons, auto-refresh, and accessible focus states.
  • Shadow DOM isolation so a host site's CSS cannot break the Liveboard.

The public renderer deliberately does not display model names, query IDs, dataset names, SQL, or internal diagnostic metadata.

Ask Your Data response contract

askInsight returns the same governed result used by Centrinfo Web. The SDK does not run a second analytics algorithm or ask the browser to infer meaning from raw rows. The response includes:

  • analysisIntent: the selected method, metric, breakdowns, comparison, requested outputs/entities, and status.
  • analysisCoverage: whether every requested aspect has deterministic evidence, plus any missing aspects.
  • businessAnalysis: calculated evidence, tables, charts, confidence, quality, provenance, and the method-specific explanation.
  • explanation: the direct reader-facing answer, measured evidence, bounded business meaning, unproven drivers, recommendations, forecast metadata, limitations, and follow-up questions.
  • chartRecommendation: the backend-selected renderer-neutral chart contract.
  • conversationId: the thread identifier to reuse for follow-up questions.

The built-in mounts display this contract automatically. For a custom UI, use the exported presentation helpers:

import {
  CentrinfoAnalyticsClient,
  resolveAnalyticsExplanation,
  resolveAnalyticsPresentationResult,
  type AnalyticsQueryResult,
} from '@accurix/centrinfo-analytics';

const result: AnalyticsQueryResult = await analytics.askInsight(answerId, {
  question: 'Segment patients by utilization and describe each group.',
  conversationId,
});

// Selects backend-provided derived artifacts for methods such as clustering,
// regression, and association rules. It does not calculate new evidence.
const presentation = resolveAnalyticsPresentationResult(result);
const explanation = result.businessAnalysis?.explanation ?? resolveAnalyticsExplanation(result);

renderResult(presentation);
renderExplanation(explanation);

The @accurix/centrinfo/liveboards entry re-exports the same result, intent, coverage, business-analysis, and explanation types. @accurix/centrinfo-results is intentionally limited to chart/table/formatting primitives and does not own the Ask transport or analytics logic.

Security model

Create an embed app in Centrinfo for each customer application. Centrinfo stores its public Ed25519 key and returns the private key once. Keep that private key in your backend or secret manager. Your backend signs a short-lived JWT for the signed-in customer; the browser only receives that token.

Tokens use EdDSA, audience centrinfo-analytics, the embed-app ID as issuer, and a lifetime of no more than 15 minutes. Include the organization ID, your stable external user ID, the allowed scopes, and only scalar external context values. Centrinfo also checks the browser Origin and the app's Liveboard/Answer allowlist on every request.

Browser usage

import {
  CentrinfoAnalyticsClient,
  mountAnswer,
  mountLiveboard,
  mountLiveboardCollection,
} from '@accurix/centrinfo-analytics';

const analytics = new CentrinfoAnalyticsClient({
  apiBaseUrl: 'https://api.example.com',
  embedAppId: 'YOUR_EMBED_APP_ID',
  getAccessToken: async () => {
    const response = await fetch('/api/centrinfo-embed-token');
    const { token } = await response.json();
    return token;
  },
});

const liveboard = mountLiveboard(analytics, '#sales-liveboard', {
  liveboardId: 'PUBLISHED_LIVEBOARD_ID',
  showTables: true,
  showExplanations: true,
  showDownload: false,
  showAskAI: true,
  allowTileExpand: true,
});

// Show every published Liveboard assigned to this SDK application.
// This selector sits above each Liveboard's own pages and tabs.
const dashboards = mountLiveboardCollection(analytics, '#customer-dashboards', {
  defaultLiveboardId: 'OPTIONAL_INITIAL_LIVEBOARD_ID',
  navigation: 'tabs',
  showTables: true,
  showAskAI: true,
});

const answer = mountAnswer(analytics, '#active-accounts', {
  insightId: 'PUBLISHED_ANSWER_ID',
  title: 'Active accounts',
});

analytics.on('error', ({ error }) => console.error(error));

// Later, if this view is removed:
liveboard.destroy();
dashboards.destroy();
answer.destroy();

Each client instance owns its token lifecycle and events, so multiple organizations or Liveboards can safely coexist on the same page.

Styling

Pass theme to CentrinfoAnalyticsClient when the embedded Liveboard should match the host application. The Liveboard renders in Shadow DOM, so prefer SDK theme tokens instead of relying on global CSS overrides.

const analytics = new CentrinfoAnalyticsClient({
  apiBaseUrl: 'https://api.example.com',
  embedAppId: 'YOUR_EMBED_APP_ID',
  getAccessToken,
  theme: {
    accent: '#2563eb',
    accentStrong: '#1d4ed8',
    accentSoft: 'rgba(37, 99, 235, 0.1)',
    background: '#f8fafc',
    surface: '#ffffff',
    surfaceMuted: '#f1f5f9',
    text: '#101827',
    mutedText: '#52647a',
    subtleText: '#7a8da3',
    border: '#dbe4ee',
    radius: '16px',
    fontFamily: 'Avenir Next, sans-serif',
  },
});

mountLiveboardCollection shows a tab or select control for every published Liveboard assigned to the SDK application. defaultLiveboardId only chooses the first selected Liveboard. navigationLabel is optional; omit it when the host application already labels the section.

Web component

The web-component entry auto-registers <centrinfo-liveboard> and <centrinfo-answer>. Pass the client through JavaScript; never place a signed token in an HTML attribute.

import { CentrinfoAnalyticsClient } from '@accurix/centrinfo-analytics';
import '@accurix/centrinfo-analytics/web-components';

const client = new CentrinfoAnalyticsClient({
  apiBaseUrl: 'https://api.example.com',
  embedAppId: 'YOUR_EMBED_APP_ID',
  getAccessToken: () =>
    fetch('/api/centrinfo-embed-token').then(async (response) => {
      const body = await response.json();
      return body.token;
    }),
});

const liveboard = document.querySelector('centrinfo-liveboard');
liveboard.configure(client, {
  liveboardId: 'PUBLISHED_LIVEBOARD_ID',
  showTables: false,
  showDownload: false,
});
<centrinfo-liveboard></centrinfo-liveboard>

React

React is an optional peer dependency. The framework-neutral renderer remains the source of truth; the React export is a small lifecycle wrapper around it.

import { CentrinfoLiveboard } from '@accurix/centrinfo-analytics/react';

export function CustomerDashboard() {
  return (
    <CentrinfoLiveboard
      client={analytics}
      liveboardId="PUBLISHED_LIVEBOARD_ID"
      showTables
      showExplanations
      allowTileExpand
    />
  );
}

Customer-facing controls

Visibility remains an organization decision. Resolve the saved organization policy on your server or in your authenticated host app and pass the approved options to the mount:

mountLiveboard(analytics, '#customer-dashboard', {
  liveboardId,
  showTables: organizationPolicy.allowTables,
  showDownload: organizationPolicy.allowDownloads,
  showAskAI: organizationPolicy.allowQuestions,
  showExplanations: organizationPolicy.allowExplanations,
  maxTableRows: organizationPolicy.maxRows,
});

These UI controls do not replace backend enforcement. The embed app's resource allowlist, signed customer identity, role restrictions, field allowlists, row security, and row limits remain authoritative.

showDownload enables card and dashboard image exports, card CSV downloads, and board-level PDF / PowerPoint exports. Reports are generated server-side by rerunning every card with the same signed customer identity used by the dashboard; internal organization data is never substituted for the customer's scoped result.

Dashboard and card actions

The default customer viewer handles refresh, native share/copy-link, image export, CSV download, PDF, PowerPoint, and Ask AI directly. It does not expose alert creation, question refinement, layout editing, copying, scheduling, or deletion.

Privileged workflows are optional callbacks. Configure them only in a trusted host that has already checked the signed-in user's permission:

mountLiveboard(analytics, '#customer-dashboard', {
  liveboardId,
  showDownload: true,
  actions: {
    createAlert: ({ tile, insight, result }) => {
      permissions.require('analytics.alerts.create');
      return alerts.open({ tileId: tile?.id, insightId: insight?.id, result });
    },
    refineQuestion: ({ insight }) => {
      permissions.require('analytics.insights.create');
      return questions.refine(insight?.id);
    },
    alertsAndDelivery: () => router.open('/analytics/alerts'),
    analyzeDashboard: ({ liveboard }) => assistant.open(liveboard?.id),
    editLayout: ({ liveboard }) => editor.open(liveboard?.id),
    refreshSettings: ({ liveboard }) => refreshRules.open(liveboard?.id),
    buildReportAndSchedule: ({ liveboard }) => schedules.open(liveboard?.id),
    makeCopy: ({ liveboard }) => liveboards.copy(liveboard?.id),
    deleteLiveboard: ({ liveboard }) => liveboards.confirmDelete(liveboard?.id),
  },
});

A callback-owned menu item is hidden when its handler is omitted. Supplying a callback controls visibility but does not grant backend permission; the host and API must still authorize the operation.

Signing claims

Use a server-side Ed25519 JWT library and sign claims shaped like:

{
  "iss": "EMBED_APP_ID",
  "aud": "centrinfo-analytics",
  "sub": "YOUR_EXTERNAL_USER_ID",
  "business_id": "optional-tenant-or-account-id",
  "context": {
    "region": "west",
    "account_id": "acct_123",
    "email": "[email protected]"
  },
  "iat": 1784190000,
  "exp": 1784190600
}

Never put the private key, token, or sensitive context in HTML, query parameters, logs, or local storage.

Customer alerts

An organization can attach an alert rule to a Liveboard exposed by the embed app. A signed-in customer must explicitly opt in before Accurix evaluates or sends that alert for their data:

const currentSubscription = await analytics.getAlertSubscription();

if (!currentSubscription.enabled) {
  await analytics.enableEmailAlerts();
}

await analytics.disableAlerts();

The token used for these calls must include alerts:subscribe. Put the customer's verified email and row-security keys in the server-signed context; the SDK never accepts an email or identity value from browser input. Alert evaluation reuses that signed context. Customer delivery currently supports email; Slack and Teams are reserved for linked organization-member identities.

Interactive demo

The demo uses realistic governed mock responses and never needs a real token:

pnpm --filter @accurix/centrinfo-analytics dev

Open http://localhost:3006. The demo includes approved filter presets, multi-series charts, KPI explanations, a limited table, downloads, per-card refresh, expansion, fullscreen, responsive layouts, and zoom controls.

Release

Run the complete local package gate:

pnpm --filter @accurix/centrinfo-analytics verify:release
pnpm --filter @accurix/centrinfo-analytics test-e2e

GitHub Actions publishes automatically when a tag matching centrinfo-analytics-v<package-version> is pushed. For version 0.1.0:

git tag centrinfo-analytics-v0.1.0
git push origin centrinfo-analytics-v0.1.0

The workflow verifies that the tag equals package.json, runs type, lint, unit, build, package, and browser checks, then publishes with the existing NPM_TOKEN repository secret. It can also be run manually in dry-run mode.