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

@insurup/table-adapter-core

v0.1.19

Published

Framework-agnostic TanStack Table adapter for InsurUp SDK with @tanstack/query-core

Readme

@insurup/table-adapter-core

Framework-agnostic TanStack Table adapter for @insurup/sdk.

This is the core package that provides the table adapter logic. For framework-specific bindings, see:

Features

  • 🚀 Framework agnostic - works with React, Vue, Svelte, Solid, or vanilla JS
  • 🔄 Built-in caching via @tanstack/query-core
  • 📝 Type-safe columns with SDK field autocompletion
  • 📄 Cursor pagination handled internally

Installation

npm install @insurup/table-adapter-core @insurup/sdk
bun add @insurup/table-adapter-core @insurup/sdk

Quick Start

import { createCustomerTable } from '@insurup/table-adapter-core';
import { DefaultInsurUpClient } from '@insurup/sdk';

const client = new DefaultInsurUpClient({
  baseUrl: 'https://api.insurup.com',
  tokenProvider: () => token,
});

// Create table adapter with builder API
const customerTable = createCustomerTable({
  columns: (col) => [
    col.id({ header: 'ID' }),
    col.name({ header: 'Name', sortable: true }),
    col.primaryEmail('Email'),
    col.createdAt({ header: 'Created', sortable: true }),
  ],
  fetch: (options) => client.customers.getCustomers(options),
  pageSize: 10,
  autoFetch: true,
  // Sorting is managed by the consumer via tableOptions.state.sorting
  tableOptions: {
    state: {
      sorting: [{ id: 'createdAt', desc: true }],
    },
    onSortingChange: (updater) => {
      // Update your sorting state here
    },
  },
});

// Subscribe to state changes
const unsubscribe = customerTable.subscribe(() => {
  const state = customerTable.getSnapshot();
  console.log('Data:', state.rows);
});

// Get TanStack Table options
const tableOptions = customerTable.getTableOptions();

// Cleanup when done
customerTable.destroy();

API

createCustomerTable

Creates a customer table adapter with built-in caching and state management.

const customerTable = createCustomerTable({
  // Required
  columns: (col) => [...],            // Builder function for columns
  fetch: (options) => Promise,        // SDK fetch function

  // Optional
  pageSize?: number,                  // Items per page (default: 20)
  defaultFilter?: UnifiedFilterInput, // Initial filter (see "Filter and search" below)
  staleTime?: number,                 // Cache stale time in ms (default: 30000)
  gcTime?: number,                    // Garbage collection time in ms (default: 300000)
  autoFetch?: boolean,                // Fetch on creation (default: false)

  // Callbacks
  onError?: (error) => void,          // Called on fetch error
  onSuccess?: (data) => void,         // Called on fetch success
  onSettled?: (data, error) => void   // Called after fetch completes
});

Filter and search

The adapter exposes a single unified filter API. There is no separate setSearch — every field in setFilter is either a regular filter or, when carrying the $search: true marker, a server-side search clause.

table.setFilter({
  // Regular filter — routed to the server's `filter:` slot
  type: { eq: CustomerType.Individual },
  birthDate: { gte: '1990-01-01' },

  // Search — `$search: true` promotes the field to the server's `search:` slot
  // and unlocks the search-only operators (`textSearch`, `wildcard`, `autocomplete`)
  name: { $search: true, textSearch: 'ali' },

  // Long form with relevance score
  identityNumber: {
    $search: true,
    textSearch: { value: '123', score: { boost: 2 } },
  },
});

String shorthand is normalized by the SDK: textSearch: 'ali' is equivalent to textSearch: { value: 'ali' }. Likewise in: ['a','b'] becomes { values: ['a','b'] }.

and / or combinators are supported recursively. Each combinator item is split per-bucket — and semantics are preserved; an or whose items mix filter and search keys becomes an implicit AND at the wire (server combines its filter: and search: slots with AND). Keep each or item homogeneous if intent matters.

To introspect what's filterable / searchable on an entity at runtime, read the generated meta:

import { QueryCustomerModelMeta } from '@insurup/sdk';

QueryCustomerModelMeta.name.filterable; // true
QueryCustomerModelMeta.name.searchable; // true
QueryCustomerModelMeta.name.filterOperators; // ['eq','neq','contains','notContains', ...]
QueryCustomerModelMeta.name.searchOperators; // ['eq','neq','textSearch','wildcard', ...]

Column Builder

The columns option receives a builder with methods for each field:

columns: (col) => [
  // Simple - uses field name as header
  col.id(),

  // With custom header
  col.name('Customer Name'),

  // With full config
  col.type({
    header: 'Type',
    sortable: true,
    render: (value) => (value === 'Individual' ? '👤' : '🏢'),
  }),

  // Computed column using multiple fields
  col.computed({
    uses: ['cityText', 'districtText'] as const,
    header: 'Location',
    render: (row) => `${row.cityText}, ${row.districtText}`,
  }),
];

Adapter Methods

| Method | Description | | --------------------- | ------------------------------------------------------------------------- | | columns | TanStack ColumnDef[] (readonly) | | getState() | Current state: { rows, rowCount, pageCount, isLoading, error, ... } | | getTableOptions() | Complete TanStack Table options (includes data, columns, getCoreRowModel) | | subscribe(listener) | Subscribe to state changes (for useSyncExternalStore) | | getSnapshot() | Get current state snapshot (for useSyncExternalStore) | | getServerSnapshot() | Get server snapshot for SSR (for useSyncExternalStore) | | fetch() | Trigger a manual fetch | | invalidate() | Invalidate cache and refetch | | refetch({ force }) | Refetch with optional cache bypass | | destroy() | Clean up resources (call on unmount) | | setFilter(filter) | Set unified filter (filter + $search-marked entries) and refetch | | getFilter() | Return the current unified filter | | clearFilter() | Clear filter and refetch | | setPageSize(size) | Change page size |

AdapterState

State returned by getState() and getSnapshot().

interface AdapterState<TEntity> {
  rows: TEntity[]; // Current page data
  rowCount: number; // Total records
  pageCount: number; // Total pages
  isLoading: boolean; // Initial loading
  isFetching: boolean; // Any fetch in progress
  error: Error | null; // Error if any
  isError: boolean;
  isSuccess: boolean;
}

Pagination Limitations

This adapter uses cursor-based pagination, which only supports sequential navigation (previous/next). Jumping to arbitrary pages (e.g., page 1 → page 5) is not supported.

The getTableOptions() method returns paginationMode: 'cursor' to signal this limitation.

License

MIT