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

@cepseudo/components

v1.0.1

Published

Core component base classes for Digital Twin framework (Collector, Harvester, Handler, CustomTableManager)

Readme

@cepseudo/components

npm version License: MIT Node.js

Core component base classes for the Digital Twin framework. Extend these to build your application.

Installation

pnpm add @cepseudo/components

Components Overview

| Component | Purpose | Scheduled | Database | HTTP Endpoints | |---|---|---|---|---| | Collector | Fetch data from external sources | Cron | Write | GET (latest data) | | Harvester | Process and transform collected data | Cron or event-driven | Read + Write | GET (latest result) | | Handler | Expose custom HTTP endpoints | No | No | User-defined | | CustomTableManager | Manage structured data tables | No | Full CRUD | Auto-generated CRUD |

Usage Examples

Collector

Collectors run on a cron schedule to fetch data from external APIs and persist it automatically.

import { Collector } from '@cepseudo/components'
import type { CollectorConfiguration } from '@cepseudo/shared'

class WeatherCollector extends Collector {
  getConfiguration(): CollectorConfiguration {
    return {
      name: 'weather-data',
      description: 'Collects weather observations from OpenMeteo',
      contentType: 'application/json',
      endpoint: 'weather'
    }
  }

  getSchedule(): string {
    return '0 */15 * * * *' // Every 15 minutes
  }

  async collect(): Promise<Buffer> {
    const response = await fetch(
      'https://api.open-meteo.com/v1/forecast?latitude=50.85&longitude=4.35&current_weather=true'
    )
    const data = await response.json()
    return Buffer.from(JSON.stringify(data))
  }
}

The framework stores the returned Buffer in object storage and indexes it in the database. A GET /weather endpoint is exposed automatically to retrieve the latest collected data.

Harvester

Harvesters process data written by other components. They can be triggered when new source data arrives (on-source) or run on a fixed schedule.

import { Harvester } from '@cepseudo/components'
import type { HarvesterConfiguration, DataRecord } from '@cepseudo/shared'

class TemperatureAverageHarvester extends Harvester {
  getUserConfiguration(): HarvesterConfiguration {
    return {
      name: 'temperature-average',
      type: 'harvester',
      description: 'Computes hourly average temperature',
      contentType: 'application/json',
      endpoint: 'temperature-average',
      source: 'weather-data',
      source_range: '1h',
      triggerMode: 'scheduled'
    }
  }

  async harvest(
    sourceData: DataRecord[],
    _dependenciesData: Record<string, DataRecord | DataRecord[] | null>
  ): Promise<Buffer> {
    const readings = await Promise.all(
      sourceData.map(async (record) => {
        const raw = await record.data()
        return JSON.parse(raw.toString())
      })
    )

    const avgTemp =
      readings.reduce((sum, r) => sum + r.current_weather.temperature, 0) /
      readings.length

    return Buffer.from(JSON.stringify({ averageTemperature: avgTemp }))
  }
}

Handler

Handlers expose stateless HTTP endpoints. They do not write to the database and are suited for real-time computations or proxy requests.

import { Handler } from '@cepseudo/components'
import { servableEndpoint } from '@cepseudo/shared'
import type { ComponentConfiguration, DataResponse } from '@cepseudo/shared'

class HealthHandler extends Handler {
  getConfiguration(): ComponentConfiguration {
    return {
      name: 'health-handler',
      type: 'handler',
      contentType: 'application/json'
    }
  }

  @servableEndpoint({ path: '/health', method: 'get' })
  async checkHealth(): Promise<DataResponse> {
    return {
      status: 200,
      content: JSON.stringify({
        status: 'ok',
        uptime: process.uptime()
      })
    }
  }
}

CustomTableManager

CustomTableManagers define structured database tables with automatic CRUD endpoints and owner-based access control.

import { CustomTableManager } from '@cepseudo/components'
import type { StoreConfiguration, DataResponse } from '@cepseudo/shared'

class SensorRegistryManager extends CustomTableManager {
  getConfiguration(): StoreConfiguration {
    return {
      name: 'sensors',
      description: 'IoT sensor registry',
      columns: {
        sensor_id: 'text unique not null',
        type: 'text not null',
        location: 'text',
        active: 'boolean default true'
      }
    }
  }
}

This generates the following endpoints automatically:

| Method | Path | Auth | Description | |---|---|---|---| | GET | /sensors | No | List all records | | POST | /sensors | Yes | Create a record (sets owner_id) | | GET | /sensors/:id | No | Get record by ID | | PUT | /sensors/:id | Yes | Update record (owner only) | | DELETE | /sensors/:id | Yes | Delete record (owner only) |

You can add custom endpoints alongside the built-in CRUD ones:

getConfiguration(): StoreConfiguration {
  return {
    name: 'sensors',
    description: 'IoT sensor registry',
    columns: {
      sensor_id: 'text unique not null',
      type: 'text not null',
      active: 'boolean default true'
    },
    endpoints: [
      { path: '/by-type', method: 'get', handler: 'handleGetByType' }
    ]
  }
}

async handleGetByType(req: any): Promise<DataResponse> {
  const type = req.query?.type
  const records = await this.findByColumn('type', type)
  return {
    status: 200,
    content: JSON.stringify(records),
    headers: { 'Content-Type': 'application/json' }
  }
}

License

MIT