# @rawdash/core

[![npm version](https://img.shields.io/npm/v/@rawdash/core)](https://www.npmjs.com/package/@rawdash/core)
[![license](https://img.shields.io/npm/l/@rawdash/core)](https://github.com/rawdash/rawdash/blob/main/LICENSE)

Headless dashboard backend primitives for rawdash. Define connectors, dashboards, widgets, and metrics — no UI assumptions, works anywhere.

## What it is

`@rawdash/core` is the foundation of the rawdash ecosystem. It provides the types and functions (`defineConfig`, `defineDashboard`, `defineMetric`, `defineConnector`, `secret`) that every rawdash setup is built on. It has no framework dependencies and no I/O — it only models your dashboard configuration and the connector interface.

Other packages (`@rawdash/server`, `@rawdash/hono`, `@rawdash/sdk-nextjs`, `@rawdash/mcp`) take a config produced by this package and add runtime behavior.

## Install

```sh
npm install @rawdash/core @rawdash/connector-github
```

## Quick example

```ts
import {
  defineConfig,
  defineDashboard,
  defineMetric,
  secret,
} from '@rawdash/core';

const github = {
  name: 'github',
  connectorId: 'github-actions',
  config: {
    owner: 'my-org',
    repo: 'my-repo',
    token: secret('GITHUB_TOKEN'),
  },
};

export default defineConfig({
  connectors: [github],
  dashboards: {
    engineering: defineDashboard({
      widgets: {
        open_prs: {
          kind: 'stat',
          title: 'Open PRs',
          metric: defineMetric({
            connector: github,
            shape: 'entity',
            field: 'id',
            fn: 'count',
            filter: [{ field: 'state', op: 'eq', value: 'open' }],
          }),
        },
      },
    }),
  },
});

// When mounting the engine, register the connector class:
//   import { GitHubConnector } from '@rawdash/connector-github';
//   mountEngine(config, { connectorRegistry: { 'github-actions': GitHubConnector } });
```

## API

### `defineConfig(config)`

Validates and returns a `DashboardConfig`. Throws if a widget references an unknown connector or uses invalid shape/fn values.

### `defineDashboard({ widgets })`

Validates and returns a `Dashboard`. Widget keys must be URL-safe (`[a-zA-Z0-9_-]`).

### `defineMetric(options)`

Returns a `ComputedMetric` that can be used in stat, timeseries, and distribution widgets.

### `secret(name)`

Returns a `Secret` that resolves the named environment variable at runtime. Connectors that accept a `token` or API key should use this to avoid hardcoding credentials.

### `defineConnector(options)`

Low-level factory for authoring new connectors. Used by packages like `@rawdash/connector-github`.

### `planSync(input)`

Pure scheduling helper that decides whether a sync should re-fetch windowed history or run a cheap incremental pass. The engine owns this policy so every integrator (self-hosted `runSync`, the hosted product, your own runner) makes the same cost-aware decision instead of reinventing it.

```ts
planSync({
  lastSyncAt, // Date | null — when this connector last synced
  lastBackfillAt, // Date | null — when it last re-fetched windowed history
  fetchSpecs, // the connector's fetch specs (carry requiredWindowMs)
  now, // Date
  cadenceMs, // optional; defaults to BACKFILL_CADENCE_MS (1h)
}); // → { mode: 'full' | 'latest', options: SyncOptions, backfillDue: boolean }
```

- `mode` is `'full'` on the connector's first sync, or when a spec declares `requiredWindowMs` and the last windowed backfill is older than `cadenceMs`; otherwise `'latest'` (with `options.since` set to `lastSyncAt` for an incremental pass).
- `backfillDue` is `true` exactly when the run fetches windowed history — persist your `lastBackfillAt` only then.

Feed it **per-connector** history: build `fetchSpecs` with `fetchSpecsForConnector(config, connectorName)` and pass that connector's own `lastSyncAt` / `lastBackfillAt` (the `SyncSchedulingState` shape) so a newly added connector still backfills its window instead of inheriting another's timestamps. See [`@rawdash/server` → Windowed-backfill scheduling](../server/README.md#windowed-backfill-scheduling) for how the self-hosted runner persists this via `getConnectorSyncState` / `markConnectorSyncSucceeded`.

For an end-to-end guide on building a new connector (shapes, settings, chunked syncs, rate limits, testing, publishing), see [docs/authoring-a-connector.md](../../docs/authoring-a-connector.md).

## Links

- [rawdash docs](https://rawdash.dev)
- [GitHub](https://github.com/rawdash/rawdash)
- [Issues](https://github.com/rawdash/rawdash/issues)

## License

Apache-2.0
