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

@aintela/bi

v0.2.9

Published

BI visualisation components, dashboard grid, hooks, the dashboard composer (sketch pad + authoring view) and the LLM runtime registry for the AIA platform (extracted from Envision's dashboard app).

Readme

@aintela/bi

React BI components for the AIA platform, extracted from Envision's dashboard app (Issue 1210, plan in AIA-Solution/Docs/BI/BiLibrary_ExtractionPlan.md §1.2). It pairs with @aintela/chat: the chat library runs the LLM-emitted TSX, this package is what that TSX imports.

@aintela/bi            flat union of every subpath — what LLM code imports
@aintela/bi/data       DataResult, defineSchema / InferModel, coercers, formatters, computeDomain/Ticks, trend helpers
@aintela/bi/cards      ChartCard, DataTableCard (MUI X Pro), MetricCard, MetricGroupCard, GaugeCard, GaugeGroup, TreeMapCard, MapBubblesCard, MapConnectionsCard
@aintela/bi/grid       GridLayout, GridCard, GridCardPlaceHolder, useGridConfigTranslator, BiErrorBoundary
@aintela/bi/inline     Chart (PNG artifact card), Metric, MetricGroup, IFrame — the explorer's present_* vocabulary
@aintela/bi/hooks      useDashboardData, useDashboardLazyData, DashboardParametersProvider
@aintela/bi/filters    VizAutoComplete, VizDateRangePicker, VizDatePicker, VizSelect, VizTextField, VizTriggerButton, createFilterComponent
@aintela/bi/viewer     useDashboardBundle (headless), ScriptDashboardViewer, ScriptDashboardViewerVertical
@aintela/bi/runtime    registerBiModules, createBiUiTools, loadDashboardBundle / instantiateDashboardBundle, BI_RUNTIME_VERSION
@aintela/bi/theme      createBiTheme / withBiPalette, useBiPalette, biSeriesColor, BiThemeBridge, biLightPalette, biDarkPalette
@aintela/bi/client     createBiClient, BiClientProvider, useBiClient (+ the Dashboard / DashboardFolder document routes)
@aintela/bi/composer   ComposerView, DashboardPad, DashboardPadSketch, ComposerProvider / useComposer, the LLM pad helpers, the environment API
@aintela/bi/style.css  the grid + inline + composer stylesheet (token-driven, no colours of its own)

Install

npm i @aintela/bi @aintela/chat

Peer dependencies (the host installs them, this package never bundles them).

Since 0.2.9 there are no optional peers. There used to be four — framer-motion, zustand and the two date pickers — and the marking was wrong for all of them: each is loaded EAGERLY, by a static import, from at least two published entry points. Marking a peer optional tells npm not to install it and not to warn, so the only thing it bought was a package that throws on its first line:

Cannot find package '<peer>' imported from …/dist/index-*.js

Nothing catches that earlier. npm is satisfied (the peer was optional), and tsc is satisfied (types resolve from the .d.ts; only a real module load pulls the chunk). It cost the AIA agentserver image its whole dashboard composer — the baked react-tsx-template declared zustand and the date pickers but happened not to declare framer-motion, so every LLM-authored card test died on its first import { … } from '@aintela/bi'. Only that one peer surfaced, and only because the other three were declared by luck; a consumer with none of them installed throws on whichever the bundler resolves first. A developer checkout can never show any of it, because there the consumer resolves peers from this workspace's own node_modules.

What "optional" was reaching for is real but per ENTRY POINT, which npm cannot express — so it lives in the table below and in tests/peerReachability.test.ts, which pins it and fails if it moves:

| entry | eagerly needs | |---|---| | @aintela/bi (the flat union), /runtime, /composer | framer-motion, zustand, both date pickers | | /filters, /viewer | both date pickers | | /cards, /grid, /data, /inline, /hooks, /theme, /client | nothing beyond the always-required peers |

So a consumer that imports only those seven subpaths installs four packages it does not use. That is the deliberate trade: being asked for something you may not need beats a package that throws on import, and this package already required one paid MUI X peer (@mui/x-data-grid-pro) unconditionally, so the precedent for carrying a peer you might not exercise was already set.

| peer | version | note | |---|---|---| | react, react-dom | ^18.2 | React 18 — @types/react must be 18 too | | @mui/material, @mui/icons-material | ^7 | | | @emotion/react, @emotion/styled | ^11.14 | | | @mui/x-data-grid-pro | ^8 | Pro — see the licence note | | @mui/x-date-pickers, @mui/x-date-pickers-pro | ^8 | Pro — eager on 5 of 12 entries (/filters, /viewer and the three below) | | recharts | ^2.15 | | | react-grid-layout | ^2.2 | the 2.x major with the /core subpath | | react-simple-maps | ^3 | | | dayjs | ^1.11 | | | zustand | ^5 | eager on the bare entry, /runtime and /composer — the composer creates its store with it | | framer-motion | ^11.18 || ^12 | eager on the bare entry, /runtime and /composer (the splash ↔ execution morph, the side panel, the sketch chips) | | @aintela/chat | ^0.4.25 | the runtime subpath (registry, uiTools) and the composer subpath (PromptV2, EnvironmentProvider, the v2 icons) import it at runtime |

Bundler note: dedupe react, react-dom, @mui/material, @mui/material/styles, @mui/icons-material, @emotion/react, @emotion/styled, zustand across @aintela/bi and @aintela/chat (obtapp's vite.config.ts already does this for the chat library — add @aintela/bi). Otherwise useTheme() reads a different ThemeContext than the one you created the theme in.

MUI X licence

DataTableCard is MUI X DataGrid Pro. The licence key is the host's, set once before the first render:

import { LicenseInfo } from '@mui/x-license';
LicenseInfo.setLicenseKey(import.meta.env.VITE_MUI_LICENSE_KEY);

This package ships no key. If none is set the grid renders MUI's watermark and the card logs one [@aintela/bi] MUI X licence key not set … warning per session.

Wiring a host

import { LicenseInfo } from '@mui/x-license';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { AiChatProvider } from '@aintela/chat';
import { createBiClient, BiClientProvider, createBiTheme, BiThemeBridge, registerBiModules, createBiUiTools } from '@aintela/bi';
import '@aintela/bi/style.css';

LicenseInfo.setLicenseKey(MUI_KEY);

// 1. Make the library resolvable from LLM-generated code (once, at boot).
registerBiModules({ specifiers: ['@aintela/bi'] });
// envision-abi keeps its legacy keys alive forever:
// registerBiModules({ specifiers: ['@aintela/bi', '@envision/ui-library', '@envision/ui-library/components', '@envision/aichat'], extraScope: VizLibraryShim });

// 2. One client per server. Routes default to the legacy WSServer layout; override what differs.
const bi = createBiClient({
    baseUrl: import.meta.env.VITE_BI_BASE_URL,
    getAuthHeader: () => `Bearer ${getAccessToken()}`,
    routes: { artifact: (src, { sessionId }) => `${AGENTS_URL}/artifacts/${sessionId}/${src}` },
});

// 3. present_chart / present_metric / present_metric_group / present_iframe; the host adds the rest.
const uiTools = createBiUiTools({ client: bi })
    .render('present_dashboard', (a) => <ScriptDashboardViewer dashboardId={a.id} />)
    .build();

// 4. Theme: your theme + the BI palette (light and dark defaults ship; set `palette.bi` to override).
const theme = createBiTheme(createTheme({ palette: { mode: 'light', primary: { main: BRAND } } }));

<ThemeProvider theme={theme}>
  <BiThemeBridge>                          {/* publishes the --bi-* CSS variables the stylesheet reads */}
    <BiClientProvider client={bi} sessionId={sessionId}>
      <AiChatProvider uiTools={uiTools} …/>
    </BiClientProvider>
  </BiThemeBridge>
</ThemeProvider>

registerBiModules calls the chat library's extendModuleRegistry(specifier, surface) once per specifier, extendDefaultScope(surface) once, and seeds the dashboard-bundle loader's require() map under the same strings. BI_RUNTIME_VERSION is the package version — log a mismatch against the server's AIntelA.Bi.Agents at handshake.

BiClientProvider / createBiClient

Every server call in the package goes through one BiClient: useDashboardData, useDashboardLazyData (page, distinct values, CSV/XLSX export), VizAutoComplete options, useDashboardBundle (dashboard document, bundle code, sub-agent status) and the inline Chart / IFrame URLs (client.artifactUrl(src, { sessionId?, dashboardId? })). Nothing reads process.env, import.meta.env or a hard-coded host. fetch is injectable (tests, custom transports); getAuthHeader may be async.

Data hooks

const { toDataResult, loading } = useDashboardData('<query hash>');   // inside a DashboardParametersProvider
const data = toDataResult({ month: 'date', spend: 'number' });
<ChartCard data={data} type="barV" xAxis="month" series={[{ dataKey: 'spend' }]} loading={loading} />

const lazy = useDashboardLazyData('<view id>', { airline: { type: 'string', values: 'distinct' }, total: 'number' });
<DataTableCard lazyData={lazy} />                                       // server-side page / sort / filter / export

Theming contract (plan §2.3, decision D8)

No colour literal exists in this package outside src/theme/biPalette.ts. Components read the MUI theme (theme.palette.divider, text.secondary, action.hover, background.paper, alpha(text.primary, 0.08) for translucent overlays, error/warning/success for gauge bands); the two stylesheets read --bi-* custom properties that <BiThemeBridge/> (or useBiCssVars()) derives from the theme; DataGrid Pro is styled through sx tokens.

The series palette is a theme augmentation:

declare module '@mui/material/styles' {
  interface Palette { bi: { series: string[]; positive: string; negative: string; neutral: string } }
}

createBiTheme(theme) / withBiPalette(theme) fill palette.bi from biLightPalette or biDarkPalette by palette.mode when the host did not set it; useBiPalette() and biSeriesColor(theme, i) read it; ChartCardDefaultsProvider palette={…} is the per-dashboard override.

Enforced three ways: ESLint no-restricted-syntax on #hex / rgb(a) / hsl(a) literals in .ts/.tsx (src/theme/biPalette.ts is the only override), stylelint color-no-hex + color-named: never + function-disallowed-list on .css, and tests/theme/colourLiterals.test.ts, which walks src/ and fails on any literal outside the palette file.

Composer (@aintela/bi/composer)

The authoring UI extracted from the dashboard app's components/dashboardPad/** + views/composer/** (plan §1.2, Phase 4 item 20): the sketch pad the user draws a dashboard on, the composer screen around it, and the DOM "environment" the dashboard agent drives through action tools and live TSX.

Providers and what the host still owns

import { AiChatProvider, AiChatV2Pane, RunProgressPanelV2, RunProgressStripV2, EnvironmentProvider, LiveTsxConfigProvider } from '@aintela/chat/components';
import { BiClientProvider, createBiClient, createBiUiTools } from '@aintela/bi';
import { ComposerProvider, ComposerView, ComposerChatBinding, useComposerChatBridge, useComposerEnvironment } from '@aintela/bi/composer';

function ComposerScreen({ routeSessionId, navigate }) {
  return (
    <BiClientProvider client={bi}>
      <ComposerProvider>                       {/* one zustand store per instance — no module singleton */}
        <ComposerHost routeSessionId={routeSessionId} navigate={navigate} />
      </ComposerProvider>
    </BiClientProvider>
  );
}

function ComposerHost({ routeSessionId, navigate }) {
  const bridge = useComposerChatBridge();       // onRenderPrompt / onTurnOpen / onTurnComplete / ingestToolEvent / environment
  const uiTools = useMemo(() => createBiUiTools({ client: bi, environment: bridge.environment })
      .render('present_dashboard', (a) => <ScriptDashboardViewer dashboardId={a.id} />)
      .build(), [bridge.environment]);

  return (
    <LiveTsxConfigProvider maxCharsForFull={10000}>
      <AiChatProvider
        key={routeSessionId ?? 'new'}
        agentContext={{ agentConfig: { id: DASHBOARD_AGENT_ID, name: 'Dashboard Agent' }, customer }}   // the host picks the agent
        serverBaseUrl={SERVER} sessionId={routeSessionId} user={user} storageService={storage} eventBusService={bus}
        uiTools={uiTools} hidePromptInput autoCreateSession
        onRenderPrompt={bridge.onRenderPrompt} onTurnOpen={bridge.onTurnOpen} onTurnComplete={bridge.onTurnComplete}
      >
        <EnvironmentProvider value={bridge.environment}>
          <ComposerChatBinding />               {/* mirrors the live session id + forwards gateway tool events */}
          <ComposerView
            routeSessionId={routeSessionId}
            sessionId={liveSessionId}          {/* or let ComposerChatBinding set it in the store */}
            chatPane={<AiChatV2Pane hideHeader />}
            renderProgressPanel={({ hasDashboard, dashboardCollapsed, turnOpen }) => (
              <RunProgressPanelV2 width={hasDashboard ? '22%' : '30%'} defaultCollapsed={(!dashboardCollapsed || !turnOpen) && hasDashboard} />
            )}
            progressStrip={<RunProgressStripV2 />}
            onSelectSession={(sid) => navigate(`/composer/${sid}`)}
            onDashboardNameChange={setBreadcrumb}
            datasetControls={isAdmin && <DatasetParameterizationControls orientation="column" datasets={datasets} agents={agents} agentId={agentId} onAgentChange={pickAgent} />}
          />
        </EnvironmentProvider>
      </AiChatProvider>
    </LiveTsxConfigProvider>
  );
}

What the host owns, deliberately: AiChatProvider / AiChatV2Pane / the progress panel (the chat is not inside the package); the agent ids (agentContext) — the legacy DEFAULT_DASHBOARD_AGENT_ID and the customer/Envision agent pair are gone, DatasetParameterizationControls takes agents + agentId + onAgentChange; routing (routeSessionId in, onSelectSession / onSearchAllSessions out); the breadcrumb (onDashboardNameChange); isAdmin (render datasetControls or not); LiveTsxConfigProvider; the pt-BR dayjs locale (the composer never calls dayjs.locale); the "Abrir" picker body on DashboardPad (renderDashboardPicker); Markdown for card descriptions (renderDescription — default is plain text).

Components

| export | what it is | key props | |---|---|---| | ComposerView | splash → execution layout, drafts, guided tour | chatPane (required), sessionId, routeSessionId, renderProgressPanel, progressStrip, onBeforeSend, resolveGuideElement, suggestions, templates, recentSessions, onSelectSession, onSearchAllSessions, onDashboardNameChange, datasetControls, renderDescription, sidePanelWidth, sidePanel | | DashboardPad | the V1 pad: toolbar (Sketch/Dashboard, ToolBox, Novo/Salvar/Atualizar/Abrir/Compose) + sketch or viewer | sessionId, dashboardId, turnOpen, onCompose, onChange, onBack, renderDashboardPicker, showDebug, headerExtra, renderDescription | | DashboardPadSketch | the grid of CardSketch cells + filter chips (ref: DashboardPadHandle = addGenericCell / addTypedCell / handleAddFilter) | hideInternalHeader, hideFiltersInSketch, zoomLevel, onAddCard, headerExtra, renderDescription | | CardSketch (alias CardScketch), FilterSketch, TitleSketch (aliases TextSketh, TitleSkath), ToolBox, HoverButton, ClonedComponent | the pad's pieces | | | DashboardPadEditor | tabbed shell (DashboardPadEditorHandle) | renderContent, addMenuItems, tabs, onTab* | | DatasetParameterizationControls | admin block: "DataSet Dinâmico" toggle + default dataset + agent picker | orientation, datasets, defaultTemplateDataset, agents, agentId, recommendedAgentId, onAgentChange | | DrawablePrompt | PromptV2 with the sketch in its topSlot (rail + pad, the sketch chips, the queued-changes chip) | onCreateSketch, onBeforeSend, sketchExpanded, onSketchExpandedChange, seedDraft, compact, zoomLevel, datasetControls | | EntryEmptyState, SpotlightOverlay, DashboardSidePanel, DashboardPin | the splash, the tour, the two dashboard surfaces | suggestions/templates/onSearchAllSessions; steps/resolveElement; width/defaultCollapsed | | SessionVisualizationsPanel | side panel listing the session's charts/KPIs that have scrolled OUT of the transcript | width, title, footerAction, headerActions | | CardTypeIconsProvider / useCardTypeIcons | swap the pad's icons (category / specificType / filterType maps; MUI defaults) | |

Data that used to be hardcoded is now a default you can replace: defaultComposerSuggestions ({ label, prompt }[]), defaultComposerTemplates (+ seedFromTemplate, TEMPLATE_KIND_TO_SKETCH), CARD_TYPE_CATALOG, FILTER_TYPE_CATALOG — all Portuguese travel-BI text.

The side panel is a SLOT

ComposerView's sidePanel prop takes a node, or a function of { hasDashboard, turnOpen }. Omit it and you get DashboardSidePanel, which is what every host had before the prop existed; pass null for no panel at all.

It is a slot rather than a 'dashboard' | 'visualizations' enum on purpose. A host changing its layout or its colours must not need a change in here — an enum makes every new panel a release of this package, which is the coupling that rule exists to avoid. The same reasoning is why SessionVisualizationsPanel reads a PUBLIC hook: a host that wants a different panel writes one against useSessionVisualizations() and never forks this file.

Session visualizations (@aintela/bi/runtime)

What the conversation has DRAWN, and which of it is currently on screen. The rule the panel implements: the charts live in the chat, and the panel lists only the ones that have scrolled out of view — listing all of them would be a second copy of the transcript, listing none would leave the column empty.

| export | what it is | |---|---| | SessionVisualizationsProvider | holds the registry for one conversation. Mount it around the transcript AND whatever reads the list | | VisualizationFrame | wraps one drawn thing: registers it, stamps data-viz="<callId>", watches its own intersection with the scroller. Adds no styling of its own | | useSessionVisualizations() | { items, offscreen, scrollTo(id), setScroller(el) } |

createBiUiTools already wraps present_chart, present_metric, present_metric_group and present_iframe in a frame, with the chart's own image as the panel's thumbnail. Outside a provider every piece is inert and the frame renders its child unchanged, so a host that does not want the feature sees no difference.

ComposerView finds the transcript's scroller inside the chatPane you gave it and publishes it, so the composer needs no wiring beyond the provider. A flash is drawn by the host: the frame sets data-viz-focus for two seconds and styling it is a CSS rule, not a prop.

Store — ComposerProvider, useComposer()

ComposerProvider creates one zustand store (createComposerStore) per mount; useComposer(selector) subscribes, useComposerStore() gives the API for getState() in handlers, useComposerEnvironment() returns the DashboardEnvironment. The store keeps the legacy semantics: dashboardDefinition (live), publishedDashboard (what the viewer renders — advances VIEWER_COMMIT_DEBOUNCE_MS = 3 s after the last edit, never mid-turn, immediately on commitForSend() / load / dashboardGenerated / refreshDashboard), lastSentDashboard (the queued-changes chip's baseline), renderGeneration (bundle CODE changed → codeGeneration), llmCounter (→ llmRenderCount), agentPulseTick, todosByCardKey, recordSketchChange / clearClientSideChanges (the per-turn hint the prompt carries), the guide state, and the autosave (DRAFT_SAVE_DEBOUNCE_MS = 800 ms, POST /Dashboard through the BiClient; client={null} disables it).

The environment object

DashboardEnvironment = highlightComponent(target, { durationMs, scrollIntoView }), cloneComponent(target, { includeHidden }), createDashboardPad(def), updateDashboardPad(def), refreshDashboard(), showGuide(steps, options), dismissGuide(). Targets resolve against [data-env-id], [data-env-key], [data-env-name], #id, .class or a raw selector (resolveEnvironmentElements, resolveGuideElement). Pass it to EnvironmentProvider (live TSX calls useEnvironment()), and to createBiUiTools({ environment }), which then also registers the agent's three ACTION tools: refresh_dashboard, present_dashboard_pad ({ definitionJson, mode: 'create' | 'update' }) and present_guide ({ stepsJson }). getBiModuleSurface() exposes the whole subpath, so LLM code reaches ExamplePad, ExampleCardSketh (legacy spelling kept as an alias of ExampleCardSketch), PrototypePad, useDashboardPad, c, dateRange, datePicker, autoComplete, EnvironmentProvider, useEnvironment under every registered specifier.

Theming

Same contract as the rest of the package. composer.css reads --bi-* only; the bridge gained --bi-secondary, --bi-secondary-soft, --bi-sketch-dash, --bi-sketch-dash-faint, --bi-sketch-selection (alpha(primary, .12)), --bi-scrim, --bi-highlight-outline, --bi-highlight-glow. The v2 chat theme's extra tokens (palette.surface.raised, palette.surface.hover, palette.border.soft, typography.fontFamilyMono) are read when present and fall back to background.paper / action.hover / divider / a monospace stack (resolveComposerSurfaces). Framer-motion timings are unchanged; every animated colour is a theme token.

What changed from the dashboard app

  • Providers now required: BiClientProvider (or a client prop) for every hook / viewer / inline component; DashboardParametersProvider around cards (as before); BiThemeBridge for the stylesheets to have colours.
  • Removed exports: CHART_COLORS, chartColor, chartShade (from data; chartShade lives in cards, built on MUI alpha()), GEO_URL / DEFAULT_GEO_LAYERS (now GEO_URL_WORLD / worldGeoLayers / brazilGeoLayers + GeoLayersProvider), the VizLibrary compat pieces, ContainTable, DataAnalysis.
  • Signature changes: loadDashboardBundle(client, sessionId, id) / instantiateDashboardBundle(client, …) take the client first; Chart lost partial, runaround, showHeader, eventBusService, parentComponent and gained onExpand; IFrame gained onExpand / height; VizTriggerButton requires onTrigger; VizDatePicker / VizSelect / VizTextField read DashboardParametersContext (the Python DashboardContext is gone).
  • Viewer: ScriptDashboardViewer takes onExportPdf, onSchedule, onEdit, onLayoutCommit, datasetOverride, isAdmin, datasetOverrideControl, resolveCardIcon, isPrintPreview instead of reading the composer / app stores, react-router, react-js-cron, useCustomers and the PDF endpoint. headerRender is unchanged. dashboardWaiting gained sessionId.
  • Map cards default to the world base layer only; pass geoUrl={brazilGeoLayers} (or a GeoLayersProvider) for the legacy world + Brazil-states pair.
  • loading is optional on every card (default false).
  • Metric.description renders as plain text (the app rendered Markdown).
  • Portuguese UI strings are kept as they were.
  • Composer: useComposerViewStore (module singleton, devtools) → a per-ComposerProvider store; DashboardServiceBiClient (listDashboards, getDashboardBySession, upsertDashboard, updateDashboard, deleteDashboard, dashboardAction, listFolders / getFolder / createFolder / updateFolder / deleteFolder / folderAction, getDashboardHierarchy; the draft envelope lives in toDraftPayload / fromUpsertResponse); ComposerViewV2ComposerView + useComposerChatBridge (no AiChatProvider, no router, no useDashboardAppStore); ExampleFilterSketch and TestCard were not carried over (unused); CardSketch renders the description as plain text unless renderDescription is given (was react-markdown); DashboardPad's debug panel is a <pre> (was MarkdownExtended); the "Buscar todas as sessões" link fires onSearchAllSessions instead of clicking [data-v2-search-trigger]; SketchCardItem.muiIconName now stores the icon's export name (was the constant 'SvgIcon').

Scripts

npm run build (vite lib + .d.ts) · npm test · npm run lint (eslint + stylelint) · npm run typecheck.