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

@esvit/tiny-bi

v0.0.1

Published

1. [Overview](#overview) 2. [Request Structure](#request-structure) 3. [Response Structure](#response-structure) 4. [Chart Types](#chart-types) 5. [Server](#server) 6. [Datasource](#datasource) 7. [Filter](#filter) 8. [Query](#query)

Readme

BI Tool

  1. Overview
  2. Request Structure
  3. Response Structure
  4. Chart Types
  5. Server
  6. Datasource
  7. Filter
  8. Query

Overview

Складається з двох частин:

  • бекенд, який відпрацьовує запити до бази даних та повертає дані у форматі JSON
  • фронтенд, який відображає дані у вигляді таблиць та графіків

Бекенд може бути 2 типів:

  • опрацювання кастомізованих запитів, які передаються з фронтенду
  • захаркожена логіка, яка виконує запити до бази даних та повертає дані у форматі JSON

Схема роботи виглядає наступним чином:

sequenceDiagram
    actor User as Користувач
    participant Widget as Віджет
    participant Server as Сервер
    participant Lib as Бібліотека tiny-bi
    participant DB as База даних

    Note over Server: Реалізовується поза бібліотекою

    User->>Widget: Або створює віджет через конфігуратор
    User->>Widget: Або відкриває віджет в дашборді
    Widget->>Server: Надсилає конфігурацію графіку
    Server->>Lib: Передає дані конфігурації (jsonToSql)
    Lib->>Lib: Зчитує та валідує дані (схема)
    Lib->>Server: Повертає SQL запит
    Server->>DB: Сервер виконує SQL запит
    DB->>Server: Повертає результати
    Server->>Lib: Передає результати для форматування (dataToJson)
    Lib->>Server: Повертає відформатовані дані
    Server->>Widget: Передає результат у віджет
    Widget->>User: Милується результатом

Приклад реалізації сервера: example/index.js Приклад реалізації фронта: example/index.html

Request Structure

Запит до бібліотеки tiny-bi для побудови графіків має наступну базову структуру:

{
  "type": "chart_type", // тип графіку, наприклад "bar", "line", "kpi" тощо
  "source": "table_name", // основний datasource, з якого будуть братися дані
  "dimensions": {
    "rows": [ // масив полів для рядків
      { "name": "field_name", "source": "table_name" }
    ],
    "columns": [ // масив полів для стовпців
      { "name": "field_name", "source": "table_name" }
    ]
  },
  "measures": [ // масив полів для вимірювань
    { "name": "field_name", "source": "table_name", "grouping": "sum" }
  ],
  "filter": { // обʼєкт фільтру, який буде застосовано до запиту
    // описано далі по тексту
  }
}

Response Structure

Відповідь від бібліотеки tiny-bi має наступну структуру:

{
  "type": "chart_type", // тип графіку, наприклад "bar", "line", "kpi" тощо
  "source": "table_name", // основний datasource, з якого будуть братися дані
  // ... інші поля, які були в запиті, повторюються тут щоб фронтенд міг будувати графік знаючи лише відповідь без запиту
  
  // поля, які додаються до відповіді
  "data": [ // масив масивів чисел, який містить дані для відображення
    [1, 2, 3], // перший рядок даних
    [4, 5, 6], // другий рядок даних
  ],
  "meta": { // метадані, які можуть бути корисні для відображення
    "page": 1, // номер сторінки
    "size": 25, // кількість рядків на сторінці
    "total": 100, // кількість рядків всього
  }
}

Chart Types

KPI

Відображає значення метрики та її зміну відносно попереднього періоду.

Конфігурація для KPI виглядає наступним чином:

{
  "type": "kpi",
  "measures": {
    "value": {
      "field": { "name": "amount", "source": "transactions", "grouping": "sum" }
    },
    "compareValue": {
      "field": { "name": "amount", "source": "transactions", "grouping": "sum" },
      "relativePeriod": {
        "field": { "name": "date", "source": "transactions" },
        "interval": "-1 month"
      }
    }
  },
  "filter": "-- описано далі по тексту --"
}

measures містить поле value, воно обовʼязкове, функція групування для поля обовʼязкова, описує одне поле для відображення значення метрики.

Для доходу за певний місяць запит буде виглядати наступним чином:

{
  "type": "kpi",
  "source": "transactions",
  "measures": [
    { "name": "amount", "grouping": "sum" }
  ],
  "filter": {
    "link": "and",
    "conditions": [
      {
        "field": "date",
        "operator": "between",
        "value": ["2025-01-01", "2025-01-31"]
      },
      {
        "field": "type",
        "operator": "equal",
        "value": ["income"]
      }
    ]
  }
}

Сформує SQL запит:

SELECT SUM(transactions.amount) AS value
FROM transactions
WHERE transactions.date BETWEEN '2025-01-01' AND '2025-01-31'
AND transactions.type = 'income';

Для порівняння значення з попереднім періодом, наприклад, за місяць, потрібно додати поле compareValue:

{
  "type": "kpi",
  "source": "transactions",
  "measures": {
    "value": {
      "field": { "name": "amount", "grouping": "sum" }
    },
    "compareValue": {
      "field": { "name": "amount", "grouping": "sum" },
      "relativePeriod": {
        "field": { "name": "date" },
        "interval": "-1 month"
      }
    }
  },
  "filter": {
    "link": "and",
    "conditions": [
      {
        "field": "date",
        "operator": "between",
        "value": ["2025-01-01", "2025-01-31"]
      },
      {
        "field": "type",
        "operator": "equal",
        "value": ["income"]
      }
    ]
  }
}

Server

Інтерфейс сервера має наступні методи:

async loadDatasources(path: string); // завантажує джерела даних з файлу за вказаним шляхом
async jsonToSql(json: any, tenantPayload?: any); // перетворює JSON запит у SQL запит, у tenantPayload можна передати дані про tenant, які підставляти в запит (ключ-значення)
async dataToJson(type: string, data: any, meta?: any); // перетворює дані з бази даних у JSON формат для відображення на фронтенді, meta - додаткові метадані, які можна передати

Datasource

Datasource - це джерело даних, яке містить інформацію про таблиці, поля та їх типи. Задача datasource - побудувати проєкцію даних для запитів до бази даних. В папці має бути файл index.yaml, який містить масив джерел даних. Кожне джерело даних має наступну структуру:

index.yaml:

datasources:
  transactions: transactions.yaml

transactions.yaml:

tableName: 'Transactions' # назва таблиці в базі даних
title:                    # строка або обʼєкт з локалізаціями, те що користувач буде бачити в інтерфейсі
  en: "Transactions"
  uk: "Транзакції"
condition: "TenantId = {tenantId} AND DeletedAt IS NULL" # умова, яка буде застосована до всіх запитів до цієї таблиці, tenantId буде підставлено з tenantPayload
fields:
  - name: "amount"  # назва поля в базі даних
    title:          # строка або обʼєкт з локалізаціями, те що користувач буде бачити в інтерфейсі
      en: "Amount"
      uk: "Сума"
    type: "string"  # тип поля, може бути "string", "number", "date", "boolean" тощо
  - name: "Date"
    title: 'Date'
    type: "string"
  - name: "Description"
    title: 'Comment'
    type: "string"
relations:              # масив звʼязків з іншими таблицями
  - name: 'project'     # назва звʼязку
    title: 'Project'    # заголовок, який буде показано в інтерфейсі
    to: "projects"      # назва datasource, з якою звʼязок
    field: "ProjectId"  # поле в цій таблиці, яке є зовнішнім ключем
    targetField: "Id"   # поле в цільовій таблиці, на яке вказує зовнішній ключ
    type: "n:1"         # тип звʼязку, може бути "1:1", "1:n", "n:1", "n:n"
  - name: 'counterparty'
    title: 'Counterparty'
    to: "counterparties"
    field: "CounterpartyId"
    targetField: "Id"
    type: "n:1"

Як використовувати datasource:

// 1. отримуємо datasource потрібного типу
const datasource = DataSource.getDatasource('transactions');

// 2. отримуємо поле за його назвою і шляхом, тут поле 'Name' буде шукатися в таблиці 'projectCategory', яка повʼязана з таблицею 'project', а 'project' в свою чергу повʼязана з таблицею 'transactions'
const field = datasource.getField('Name', [{ name: 'project' }, { name: 'projectCategory' }], 'sum');

// 3. отримуємо SQL запит для цього поля
const query = new Query({
  fields: [field], // сюди передаємо масив полів, які отримали з datasource
});

Filter

Перетворює json запит умов у SQL запит. На вхід приймає JSON обʼєкт, який містить поле link з можливими значеннями and, or та масивами фільтрів, які потрібно обʼєднати.

{
  "link":"and",
  "conditions":[
    {
      "field": "transactions.date",
      "operator": "between",
      "value": ["2023-01-01", "2023-12-31"]
    },
    {
      "field": "transactions.amount",
      "operator": "greater_than",
      "value": 100
    }
  ]
}

Обʼєкт з conditions має наступні поля:

  • field - поле, по якому потрібно фільтрувати
  • operator - оператор фільтрації, який може бути одним з наступних:
    • equal
    • not_equal
    • greater_than
    • less_than
    • between
    • is_null
    • is_not_null
    • starts_with
    • ends_with
    • contains
    • does_not_contain
    • matches
    • before
    • after
    • is_on
    • last
    • next
  • values - масив значень, по яких потрібно фільтрувати. Для операторів between має бути масив з 2 значень, для інших операторів - масив з 1 значенням.
  • modifier - лише для операторів next та last, може бути одним з наступних:
    • month - за наступний або попередній місяць
    • day - за наступний або попередній день
    • year - за наступний або попередній рік
    • minute - за наступну або попередню хвилину
    • hour - за наступну або попередню годину
    • week - за наступний або попередній тиждень
    • quarter - за наступний або попередній квартал

Query

Відповідає за побудову SQL запиту на основі JSON обʼєкту, який містить інформацію про джерела даних, поля, фільтри та сортування.

const query = new Query({
  tableName: 'FinanceTransactions', // основна таблиця, з якої будуть братися дані
  filter: new Filter // фільтр, який буде застосовано до запиту
  tableAlias // псевдонім таблиці, який буде використано в запиті

  limit // кількість рядків, які потрібно повернути
  offset // кількість рядків, які потрібно пропустити
  
  fields: QueryField[];
  
  noGrouping?: boolean;
  distinct?: boolean;
  fromQuery?: Query;
  fromUnions?: Query[];
  sorting?: QueryField[];
  union?: Query[];
  joins?: string[];
});

Libs

  • Odometer https://github.com/HubSpot/odometer