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

@stackwright-pro/pulse

v0.3.0-alpha.18

Published

Source-agnostic real-time data polling for Stackwright Pro

Downloads

883

Readme

@stackwright-pro/pulse

Source-agnostic real-time data polling for Stackwright Pro. Works with OpenAPI, WebSockets, GraphQL, SSE, or any data source.

License: PROPRIETARY

Features

  • 🔌 Source-Agnostic - Works with ANY async data source via a simple fetcher interface
  • ⏱️ Real-time Polling - Configurable polling intervals with automatic retries
  • 📊 State Management - Built-in states: loading, live, stale, error
  • 🛡️ Runtime Validation - Optional Zod schema validation for data integrity
  • ⚡ React Query Powered - Leverages TanStack Query for caching and background updates
  • 🎨 Flexible UI - Render props with metadata for custom state indicators

Installation

pnpm add @stackwright-pro/pulse

Peer Dependencies:

pnpm add react react-dom @tanstack/react-query

Quick Start

Basic Usage

import { Pulse, PulseIndicator } from '@stackwright-pro/pulse';

function EquipmentPanel() {
  return (
    <Pulse fetcher={() => fetch('/api/equipment').then((r) => r.json())} interval={5000}>
      {(equipment, meta) => (
        <>
          <PulseIndicator meta={meta} />
          <EquipmentGrid data={equipment} />
        </>
      )}
    </Pulse>
  );
}

With Zod Validation

import { z } from 'zod';
import { Pulse } from '@stackwright-pro/pulse';

const EquipmentSchema = z.object({
  id: z.string(),
  name: z.string(),
  status: z.enum(['online', 'offline', 'maintenance']),
  lastUpdated: z.string().datetime(),
});

function EquipmentPanel() {
  return (
    <Pulse
      fetcher={() => fetch('/api/equipment').then((r) => r.json())}
      schema={EquipmentSchema}
      interval={5000}
    >
      {(equipment) => <EquipmentGrid data={equipment} />}
    </Pulse>
  );
}

Custom State UI

import { Pulse } from '@stackwright-pro/pulse';

function CustomEquipmentPanel() {
  return (
    <Pulse
      fetcher={fetchEquipment}
      interval={5000}
      staleThreshold={30000}
      maxStaleAge={60000}
      loadingState={<SkeletonLoader />}
      errorState={(meta) => (
        <ErrorBanner
          message="Failed to load equipment"
          lastUpdated={meta.lastUpdated}
          onRetry={meta.refetch}
        />
      )}
      emptyState={<EmptyState message="No equipment found" />}
    >
      {(equipment, meta) => (
        <>
          {meta.state === 'stale' && <StaleBanner />}
          <EquipmentGrid data={equipment} />
        </>
      )}
    </Pulse>
  );
}

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      <Pulse> Component                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌─────────────┐    ┌──────────────┐    ┌───────────────┐  │
│   │  Render     │    │   usePulse   │    │ React Query  │  │
│   │  Props      │───▶│    Hook      │───▶│   Provider    │  │
│   └─────────────┘    └──────────────┘    └───────────────┘  │
│         │                   │                     │          │
│         │                   │                     │          │
│         ▼                   ▼                     ▼          │
│   ┌─────────────┐    ┌──────────────┐    ┌───────────────┐  │
│   │ PulseMeta   │    │   Fetcher    │    │   Caching &   │  │
│   │ (metadata)  │    │  Validation  │    │   Polling     │  │
│   └─────────────┘    └──────────────┘    └───────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
              ┌───────────────────────────────────┐
              │      ANY Data Source              │
              ├───────────────────────────────────┤
              │ • OpenAPI (fetch/axios)           │
              │ • WebSocket                       │
              │ • GraphQL (urql/Apollo)           │
              │ • SSE (EventSource)               │
              │ • Custom async functions          │
              └───────────────────────────────────┘

API Reference

<Pulse> Component

interface PulseProps<T> {
  // Core
  fetcher: () => Promise<T>; // Your data fetching function
  children: (data: T, meta: PulseMeta) => React.ReactNode;

  // Polling config
  interval?: number; // Poll interval in ms (default: 5000, min: 1000)
  enabled?: boolean; // Enable/disable polling (default: true)
  retryCount?: number; // Number of retries on error (default: 3)
  refetchOnWindowFocus?: boolean; // Refetch on window focus (default: true)

  // State thresholds (in milliseconds)
  staleThreshold?: number; // Show stale warning (default: 30000)
  maxStaleAge?: number; // Show error state (default: 60000)

  // Validation
  schema?: ZodSchema<T>; // Optional Zod schema

  // Custom state UI
  loadingState?: React.ReactNode;
  errorState?: React.ReactNode | ((meta: PulseMeta) => React.ReactNode);
  emptyState?: React.ReactNode;
  showStaleDataOnError?: boolean; // Show data during error (default: true)
}

PulseMeta (passed to children)

interface PulseMeta {
  lastUpdated: Date; // Timestamp of last successful fetch
  isStale: boolean; // Data older than staleThreshold
  isError: boolean; // Data older than maxStaleAge or fetch failed
  isLoading: boolean; // Currently fetching
  errorCount: number; // Number of consecutive errors
  refetch: () => void; // Manual refresh trigger
  state: 'loading' | 'live' | 'stale' | 'error';
}

<PulseIndicator> Component

interface PulseIndicatorProps {
  meta: PulseMeta; // Required meta object
  showSeconds?: boolean; // Show "Xs ago" (default: true)
  className?: string; // Additional CSS classes
  labels?: {
    live?: string; // Custom live label
    syncing?: string; // Custom syncing label
    stale?: string; // Custom stale label
    error?: string; // Custom error label
  };
}

usePulse Hook

const result = usePulse<T>({
  fetcher,
  interval = 5000,
  staleThreshold = 30000,
  maxStaleAge = 60000,
  schema,
  enabled = true,
  refetchOnWindowFocus = true,
  retryCount = 3,
});

// Returns
{
  data: T | undefined,
  meta: PulseMeta,
  state: PulseState,
  error: Error | null,
}

Source Adapters

The fetcher prop accepts any async function. This makes Pulse work with:

| Source | Example Fetcher | | ------------ | ---------------------------------------------- | | REST/OpenAPI | () => fetch('/api/data').then(r => r.json()) | | WebSocket | () => websocket.nextMessage() | | GraphQL | () => client.query({ query: MY_QUERY }) | | SSE | () => eventSource.nextEvent() |

See SOURCE_ADAPTERS.md for planned first-party adapter documentation.

State Transitions

┌──────────┐
│ loading  │ ← Initial state
└────┬─────┘
     │ data received
     ▼
  ┌──────┐
  │ live │ ← Normal operation
  └──┬───┘
     │ time > staleThreshold
     ▼
  ┌───────┐
  │ stale │ ← Data is old but usable
  └──┬───┘
     │ time > maxStaleAge OR fetch error
     ▼
  ┌───────┐
  │ error │ ← Data may be stale, show error UI
  └──┬───┘
     │ successful refetch
     ▼
  ┌──────┐
  │ live │ ← Back to normal
  └──────┘

Validation

Use Zod schemas to validate incoming data:

import { z } from 'zod';
import { createPulseValidator, PulseValidationError } from '@stackwright-pro/pulse';

const validator = createPulseValidator(MySchema);

// Safe parse - returns null on failure
const data = validator.safeParse(rawData);

// Throws PulseValidationError on failure with detailed issues
const data = validator.parse(rawData);

Best Practices

  1. Use appropriate intervals - Consider network cost vs. data freshness needs
  2. Handle empty states - Always provide emptyState for collections
  3. Validate early - Use Zod schemas to catch data contract issues
  4. Leverage meta state - Use meta.state for fine-grained UI control
  5. Cleanup properly - Disable polling when component unmounts if needed

Live Demo

See the Marine Logistics Demo for a complete example of Pulse with OpenAPI integration:

import { Pulse, PulseIndicator } from '@stackwright-pro/pulse';
import { createOpenAPIFetcher } from '@stackwright-pro/openapi';
import type { Equipment } from './generated/types';
import { EquipmentSchema } from './generated/schemas';

// Create typed fetcher from OpenAPI config
const fetcher = createOpenAPIFetcher({
  baseUrl: 'https://api.logistics.example.mil/v1',
  endpoint: '/equipment',
  auth: { type: 'bearer', token: process.env.API_TOKEN },
});

function EquipmentDashboard() {
  return (
    <Pulse
      fetcher={fetcher}
      interval={5000}
      staleThreshold={30000}
      maxStaleAge={60000}
      schema={EquipmentSchema}
      loadingState={<SkeletonLoader />}
    >
      {(equipment: Equipment[], meta) => (
        <>
          <PulseIndicator meta={meta} />
          <div className="equipment-grid">
            {equipment.map((item) => (
              <EquipmentCard key={item.id} data={item} />
            ))}
          </div>
        </>
      )}
    </Pulse>
  );
}

For a full working demo, see examples/marine-logistics-demo/src/pages/dashboard-live.tsx

Future Features

  • [ ] createWebSocketFetcher() - Real-time WebSocket adapter
  • [ ] createGraphQLFetcher() - GraphQL query polling adapter
  • [ ] createSSEFetcher() - Server-Sent Events adapter
  • [ ] Streaming mode via useStreaming hook

See docs/SOURCE_ADAPTERS.md for planned adapter documentation.

Collection Binding (Phase 3 - Killer Pro Feature)

The collection binding system connects OpenAPI dataPrebuildPulseDashboard Components for a complete live data experience.

Architecture

┌──────────────────────────────────────────────────────────────────────────┐
│                           DASHBOARD OTTER                                │
│  Generates YAML with {{ collection.field }} template syntax              │
└─────────────────────────────────┬────────────────────────────────────────┘
                                  │
                                  ▼
┌──────────────────────────────────────────────────────────────────────────┐
│                          stackwright.yml                                 │
│  integrations:                                                            │
│    - name: logistics-api                                                  │
│      spec: ./openapi.yaml                                                │
│      collections:                                                         │
│        - endpoint: /equipment                                            │
└─────────────────────────────────┬────────────────────────────────────────┘
                                  │
                                  ▼
┌──────────────────────────────────────────────────────────────────────────┐
│                         PREBUILD (pnpm prebuild)                          │
│  OpenAPIPlugin generates:                                                 │
│  - src/generated/logistics-api/provider.ts (CollectionProvider)         │
│  - src/generated/logistics-api/schemas.ts (Zod schemas)                   │
│  - src/generated/logistics-api/types.ts (TypeScript types)               │
└─────────────────────────────────┬────────────────────────────────────────┘
                                  │
                                  ▼
┌──────────────────────────────────────────────────────────────────────────┐
│                     PulseCollectionProvider                              │
│  Wraps dashboard with live collection context                            │
│  - Creates fetchers from prebuild providers                              │
│  - Polls at configured intervals (5s, 30s, 300s, etc.)                   │
│  - Provides data via React context                                       │
└─────────────────────────────────┬────────────────────────────────────────┘
                                  │
                                  ▼
┌──────────────────────────────────────────────────────────────────────────┐
│                    Pulse-Enabled Components                              │
│  MetricCardPulse, DataTablePulse, StatusBadgePulse                       │
│  - Auto-bind to collection data                                          │
│  - Support template syntax {{ collection.field }}                        │
│  - Built-in aggregation (count, sum, avg)                                │
│  - Live indicator with stale/error states                                │
└──────────────────────────────────────────────────────────────────────────┘

Quick Start

1. Configure OpenAPI in stackwright.yml

integrations:
  - name: logistics-api
    spec: ./openapi.yaml
    auth:
      type: bearer
      token: ${API_TOKEN}
    collections:
      - endpoint: /equipment
        slug_field: id
      - endpoint: /locations
        slug_field: id

2. Run Prebuild

pnpm prebuild
# Generates src/generated/logistics-api/*

3. Use Pulse Components in Pages

# pages/dashboard/content.yml
content:
  meta:
    title: 'Live Equipment Dashboard'

  content_items:
    # Wrap with Pulse provider for live data
    - type: pulse_provider
      label: equipment-live
      collections:
        - name: equipment
          endpoint: /api/equipment
          refreshInterval: 5000
      items:
        - type: grid
          columns: 4
          items:
            - type: metric_card_pulse
              collection: equipment
              field: items.length
              label: 'Total Equipment'
              icon: Truck

            - type: metric_card_pulse
              collection: equipment
              field: items
              label: 'Active'
              aggregate: count
              aggregateField: status
              filter: '{"status": "active"}'
              icon: CheckCircle

    - type: data_table_pulse
      collection: equipment
      columns:
        - field: id
          header: ID
          sortable: true
        - field: name
          header: Name
        - field: status
          header: Status
          type: badge
      sortBy: name
      sortDirection: asc
      limit: 20

Component Reference

pulse_provider

Wraps content with live collection data. All child Pulse components automatically bind to these collections.

- type: pulse_provider
  label: equipment-live
  collections:
    - name: equipment # Collection name for binding
      endpoint: /api/equipment # OpenAPI endpoint
      refreshInterval: 5000 # Poll every 5 seconds
      filter: # Optional server-side filter
        status: active
  items:
    # Child components here

Props: | Prop | Type | Description | |------|------|-------------| | label | string | Unique identifier | | collections | CollectionBinding[] | Collections to fetch | | collections[].name | string | Collection name for binding | | collections[].endpoint | string | API endpoint path | | collections[].refreshInterval | number | Poll interval in ms (default: 5000) | | collections[].filter | object | Server-side filter params |

metric_card_pulse

KPI card with auto-bound live data.

- type: metric_card_pulse
  collection: equipment # Collection to bind
  field: items.length # Field or expression
  label: 'Total Equipment' # Display label
  icon: Truck # Icon name
  aggregate: count # Optional: count, sum, avg
  aggregateField: status # Field for aggregation
  filter: '{"status": "active"}' # Optional: filter items

Props: | Prop | Type | Description | |------|------|-------------| | collection | string | Collection name | | field | string | Field path (supports dots) | | label | string | Card label | | icon | ReactNode | Icon component | | color | string | Accent color (hex) | | trend | 'up' | 'down' | 'stable' | Trend direction | | trendValue | string | Trend display value | | aggregate | 'count' | 'sum' | 'avg' | Aggregate function | | aggregateField | string | Field for aggregation | | filter | string | JSON filter string |

data_table_pulse

Sortable table with live data binding.

- type: data_table_pulse
  collection: equipment
  columns:
    - field: id
      header: ID
      sortable: true
    - field: name
      header: Name
    - field: status
      header: Status
      type: badge
  sortBy: name
  sortDirection: asc
  limit: 20

Props: | Prop | Type | Description | |------|------|-------------| | collection | string | Collection name | | columns | Column[] | Column definitions | | sortBy | string | Field to sort by | | sortDirection | 'asc' | 'desc' | Sort direction | | filter | string | JSON filter string | | limit | number | Max items to show | | onRowClick | function | Row click handler | | emptyMessage | string | Empty state message |

status_badge_pulse

Status indicator with live data binding.

- type: status_badge_pulse
  collection: equipment
  field: items[0].status
  label: 'System Status'
  pulse: true

Props: | Prop | Type | Description | |------|------|-------------| | collection | string | Collection name | | field | string | Status field path | | label | string | Display label | | pulse | boolean | Show pulse animation | | statusMap | object | Custom value → status mapping |

Hooks

For custom component integration:

import {
  usePulseCollections, // Full context
  useCollection, // Single collection
  useCollectionField, // Specific field
  useTemplateResolution, // Resolve templates
  resolveTemplate, // Static template resolver
} from '@stackwright-pro/pulse';

// Get full context
const { collections, isLoading, getField } = usePulseCollections();

// Get single collection
const equipment = useCollection('equipment');
// Returns: { items, count, meta, ... }

// Get specific field
const count = useCollectionField('equipment', 'items.length');

// Resolve template anywhere
const templateResolution = useTemplateResolution();
const value = templateResolution('{{ equipment.count }}');

Template Syntax

The {{ collection.field }} syntax is automatically resolved:

| Pattern | Example | Result | | ------------ | ------------------------------- | --------------------- | | Count | {{ equipment.count }} | Number of items | | Array length | {{ equipment.items.length }} | Array size | | Nested field | {{ equipment.items[0].name }} | First item's name | | Aggregate | {{ equipment.status.active }} | Count of active items |

ISR Intervals

Configure refresh intervals based on data freshness needs:

| Interval | Use Case | Example | | -------- | ------------- | ------------------ | | 5000ms | Real-time | Live dashboard | | 30000ms | Near-realtime | Status updates | | 300000ms | Hourly | Reports, summaries |

Error Handling

Pulse components handle errors gracefully:

// With error state
<DataTablePulse
  collection="equipment"
  columns={columns}
  fallback={<ErrorBanner />}
/>

// With stale data (continue showing data during errors)
<MetricCardPulse
  collection="equipment"
  field="count"
  showStaleData={true}
/>

Complete Example

See the marine-logistics demo for a full working example:

pnpm prebuild  # Generate providers
pnpm dev       # Start dev server

The dashboard will show live-updating KPI cards and data tables with auto-refresh!

Future: Real-time Providers

Coming soon:

  • createWebSocketFetcher() - WebSocket adapter
  • createSSEFetcher() - Server-Sent Events adapter
  • createGraphQLFetcher() - GraphQL polling adapter

License

PROPRIETARY - All rights reserved.