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

@signalsafe/data-table

v0.1.1

Published

Shared strict AG Grid tables with host-owned transports and React Query lifecycle

Readme

SignalSafe data-table

@signalsafe/data-table 0.1.1 is the shared-table release approved on 2026-10-21. One AG Grid Community component for server pages and complete client datasets. Hosts own their React Query client, validated transport, authorization, mutations and regional settings. No root barrel, API client, expression evaluator or product-specific routes are included.

Use

Import DataTable from @signalsafe/data-table/DataTable and @signalsafe/data-table/theme.css once. Import types and constants directly from configuration, query/invalidation helpers from query, and theme types from theme. React 18, React DOM 18, React Query 5 and matching AG Grid Community/React 35 or 36 instances must resolve from the host. The package never constructs a QueryClient.

import { DataTable } from '@signalsafe/data-table/DataTable';
import { DATA_MODE, FILTER_OPERATOR } from '@signalsafe/data-table/configuration';
import type { ServerAdapter, TableConfig } from '@signalsafe/data-table/configuration';
import '@signalsafe/data-table/theme.css';

interface Person {
    id: string;
    name: string;
}
const config: TableConfig<Person> = {
    id: 'people',
    caption: 'People',
    getRowId: (row) => row.id,
    columns: [
        {
            id: 'name',
            label: 'Name',
            kind: 'text',
            value: (row) => row.name,
            operators: [FILTER_OPERATOR.contains, FILTER_OPERATOR.equals],
        },
    ],
    defaultSort: [{ column: 'name', direction: 'asc' }],
    stableSortColumn: 'id',
    pageSize: 20,
    pageSizes: [20, 50, 100],
    selection: 'none',
};
// These functions belong to the host's validated API adapter.
const adapter: ServerAdapter<Person> = {
    kind: DATA_MODE.server,
    key: 'people-v1',
    read: (query, signal) => personApi.readTable(query, signal),
    decode: (cached) => personApi.decodeTableCache(cached),
};
<DataTable
    config={config}
    adapter={adapter}
    queryClient={hostQueryClient}
    scope={{ application: 'my-app', user: user.id, company: company.id }}
/>;

For fully loaded data use { kind: DATA_MODE.client, key: 'all-people-revision-1', rows: people }. This is a completeness assertion by the host: do not pass one server page or a growing pagination buffer as a client dataset. Client filtering and sorting run before slicing. Server adapters receive the full query and must implement it before pagination; the AG Grid view cannot locally sort/filter the current server page.

Configuration and behavior

Columns have stable IDs, typed value access, optional formatting/rendering, explicitly supported operators and optional integer constraints. Row/toolbar actions carry labels, icons and host permission decisions. Action callbacks remain in the host; mutates: true invalidates only that application/user/company/table after successful completion. The package rechecks the latest row and permission at click time.

Filters debounce for 350ms and reset pagination on commit; numeric zero, false, empty text and null have distinct operators. Sorting appends the declared stable tie-breaker. Query caches include application, user, company, table, adapter context and canonical query; inactive cached entries are bounded and expire. Signals reach adapter requests; inactive/unmounted consumers cancel reads, and obsolete responses do not replace a newer query.

Known totals drive exact ranges/pages; unknown totals display Total unknown and use the adapter's explicit hasNext. A deleted final row moves to the surviving final page. Refresh retains filters, sorting and page size. Loading, refreshing, empty, no-match and retryable error states remain distinct.

Selection and export distinguish current page, explicit IDs across pages and the complete matching query with exclusions. ExportRequest.pageRows is only the loaded page. Hosts resolve selected IDs or all matching records through their own validated export capability; never relabel page rows as all matching. Export requests receive an AbortSignal.

All visible strings and operator labels can be replaced with typed messages. Regional number/date callbacks belong to the host; the client date evaluator uses the supplied timezone. Theme tokens cover font, color, spacing, density, icon tones, borders and focus. Header/row/action dimensions respond to text sizing. Native controls provide labels, titles and visible focus; table content may scroll horizontally at narrow widths.

Preferences are opt-in through an injected store. Reads validate version, table/scope, allowed page sizes, sortable column IDs and optional query fields. Defaults persist only page size and sorting. persistQuery: true explicitly enables validated filters and page; no row payload is persisted. The host chooses storage; the package falls back safely when optional storage reads or writes fail.

See adoption contract and pilot evidence. Local verification is not registry publication or independent review approval.

Verification

npm run test:coverage measures the existing and added control, query-lifecycle, permission and persisted-state suites. The reporter includes all runtime TypeScript and authored scripts; scripts executed by separate package/build gates remain unmeasured in this Vitest report. Sonar also analyzes the workflow and Vitest configuration, including uncovered code rather than excluding it. The measured baseline is not a new coverage threshold.

CI tests the matching AG Grid Community/React 35.1.0 and 36.1.0 peer pairs on Node 24, then checks strict types, lint, architecture, formatting, build and packed imports. The scanner waits for the existing quality gate on trusted repository branches; fork pull requests run the package checks without receiving analysis credentials. CI analysis requires Automatic Analysis to be disabled for this Sonar project. These checks do not adopt the separate canonical pilot or replace its host/browser verification.

Release preparation

Release 0.1.1 combines the installed Step 5 features and verified audit fixes. Public distribution was approved on 2026-10-21 and the package remains UNLICENSED; see RELEASING.md for approval, peer-matrix, archive, and host verification requirements.

Selection and detail workflows

Set config.selection: 'multiple' to render accessible row checkboxes in the grid. initialSelection hydrates uncontrolled selection once. selection controls the current TableSelection; onSelection proposes changes and the host supplies the next value. Hosts own pending/applied state, URL persistence, authorization and confirmations. Explicit IDs survive page navigation. Uncontrolled selection resets when sorting or filters change; controlled explicit IDs remain host-owned. Matching selection applies only to the filter/sort identity in its query; exclusions survive page changes and cannot leak into a different query. Select current page adds its IDs to the existing explicit set. Clear selection removes both IDs and exclusions.

selectedRowId marks a domain-selected row independently of bulk selection. onActivate responds to Enter and double click; choose activateOn: 'click' for single click. Buttons, links and form controls never trigger row activation, and inactive tables do not activate. refreshKey triggers a read of the current server query when its value changes; changes while inactive refresh on reactivation. onRefresh optionally replaces the shared Refresh action with a host-owned reload workflow (including complete client arrays); failures and asynchronous busy state use the shared action handling. Hosts notify a server table afterward with refreshKey or scoped invalidation. Without this callback, Refresh rereads the current server query. Host mutation callbacks can continue using action mutates: true and scoped invalidateTable.

detail: { expandedIds, render } adds full-width rows after their parents. Content height is measured by the package, updates on resizing, and observers disconnect on removal. nestedRows(parent) supplies already validated child rows displayed after that parent and its detail; child rows use the same typed columns/actions and may have details. Parent pagination, totals, filtering and sorting remain adapter-owned; child rows do not increase the page count. Child IDs must identify the domain records uniquely for selection/expansion. Hosts own expansion state and child fetching/cancellation. Nested DataTable instances inside detail content retain their own typed adapters and scopes. Columns may declare group: {id,label}; consecutive columns in the same group share an accessible heading while keeping column filters aligned.

Pagination provides first/previous/next/last and numbered page buttons. Unknown totals disable Last; Next follows the adapter’s hasNext result, and numbered buttons cover pages already reached. Clear filters is always accessible in the top-right toolbar, including nonempty results.

Domain content and focus

emptyContent supplies a genuine product action for an unfiltered empty dataset; it is hidden during loading, failures, nonempty results and filtered empty results. Column description supplies shared header help. The package names the actual grid and connects keyboard instructions through aria-describedby; hosts may localize messages.instructions.

Stable cell renderer components preserve domain opener focus during host rerenders. Cached pages are decoded again when their observed cache revision changes, including updates that retain the same object reference. Host-owned client tables may use onRefresh to reload their complete validated dataset; server tables retain the default refetch when no callback is supplied.

toolbarContent places genuine domain controls (for example an About dialog opener) alongside shared toolbar actions. Hosts retain their product callback/content; the package owns the header, action alignment and shared button styling.

Row actions wrap within their column when necessary. Bootstrap dark body colors keep neutral header text readable; explicit header text can be set with theme.headerForeground. Saved action colors remain controlled through the shared theme tokens.

Saved appearance preferences

Import TableThemeProvider from @signalsafe/data-table/TableThemeProvider and appearancePreferences from @signalsafe/data-table/theme. Hosts decode saved { preset, colorScheme, iconSet } strings with appearancePreferences, then pass the resulting typed theme to the provider around their application. Supported presets are Quartz, Alpine, Balham and Material. Supported color schemes are light, dark, variable, light-warm, light-cold, dark-warm and dark-blue; icon sets are Bootstrap, Quartz, Material, Alpine, Quartz Bold, Quartz Light and Quartz Regular. Unknown saved values use Quartz Light with Bootstrap icons. Explicit table-local theme values override inherited values; appearance changes preserve query, page and selection state. No engine configuration escapes this API.

Row and toolbar action icons remain shared Bootstrap SVGs. The selected engine icon set controls sort indicators; Bootstrap is the default. Light uses the shared neutral palette and Bootstrap semantic colors; other selected schemes use their named palette, with explicit color overrides taking precedence.

The footer shows only the centered page indicator (Page 2/6). Page-size selection and navigation share the right group. Narrow screens stack these groups while keeping the page indicator centered.

All table captions, including Lessons and Scenarios, use the same package-owned 1.5rem heading with regular weight and shared top-left alignment.

See the current release documentation for release-specific compatibility and verification boundaries.

Static semantic tables

For a small, complete host-rendered summary, StaticTable shares the table appearance without creating an AG Grid or query lifecycle. It accepts normal table attributes and children; the host supplies semantic headings and rows. Import each declaring subpath explicitly:

import { StaticTable } from '@signalsafe/data-table/StaticTable';
import { TableThemeProvider } from '@signalsafe/data-table/TableThemeProvider';
import { appearancePreferences } from '@signalsafe/data-table/theme';

export function Summary() {
    return (
        <TableThemeProvider
            theme={appearancePreferences({
                preset: 'quartz',
                colorScheme: 'light',
                iconSet: 'bootstrap',
            })}
        >
            <StaticTable aria-label='Summary'>
                <thead>
                    <tr>
                        <th scope='col'>Item</th>
                        <th scope='col'>Count</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <th scope='row'>Complete</th>
                        <td>3</td>
                    </tr>
                </tbody>
            </StaticTable>
        </TableThemeProvider>
    );
}

Use DataTable for validated complete datasets or server pages requiring shared sorting, filters, selection or pagination. StaticTable does not infer those capabilities from its children.