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

sheetq

v1.0.1

Published

Typed, server-side query layer for Google Sheets — model a sheet like a tiny ORM: gviz-powered search, type-safe read & write.

Readme

O‘zbekcha · English

sheetq

Google Sheets uchun tipli (type-safe), server tomonda ishlovchi qidiruv qatlami — varaqni xuddi kichik ORM kabi modellashtiring.

sheetq Google jadvalini kichik, tipli ma'lumotlar ombariga aylantiradi. Siz varaq ustunlarini bir marta tavsiflaysiz va to'liq tipli model olasiz: find / insert / update / delete — qo'lda interfeys yozish ham, A1 diapazonlarini hisoblash ham shart emas.

Qidiruvlar server tomonda, Google Visualization API (gviz) orqali ishlaydi: 10 000 ta qatorni tortib olib JS'da filter qilish O'RNIGA, sheetq Google serveriga SQL'simon so'rov yuboradi va faqat mos qatorlarni qaytaradi. 10 000 qator ichidan bittasini topish = bitta so'rov.

const Users = spreadsheet
  .doc("<spreadsheetId>") // jadval URL'idagi uzun id
  .model("users", {
    columns: {
      id:    { col: "B", type: "number" },
      name:  { col: "C", type: "string" },
      email: { col: "D", type: "string" },
    },
  });

const user = await Users.findOne({ id: 5042 }, { exact: true });
//    ^? { id: number | null; name: string | null; email: string | null } | null

Imkoniyatlar

  • Sxemadan tip chiqarish — qator tipi columns dan AVTOMATIK olinadi; alohida interfeys yozmaysiz.
  • Server tomonda qidiruvfind / findOne gviz so'roviga aylanadi, faqat mos qatorlar qaytadi.
  • O'qish va yozishinsert, insertMany, update, delete, set, clear, clearRange, hamda formulani avtomatik to'ldirish.
  • Ustun bo'yicha konvertatsiyastring / number / boolean / date[:FORMAT], yoki o'zingizning transform (o'qish) va serialize (yozish) funksiyalaringiz.
  • Harf bo'yicha ustunlar — fieldlarni haqiqiy ustun harflariga bog'laysiz ("A", "B", "F"…); ustunlar orasida bo'shliq bo'lsa ham ishlaydi.
  • Yengil — faqat googleapis va dayjs ga bog'liq.

O'rnatish

yarn add sheetq
# kerakli paketlar avtomatik o'rnatiladi: googleapis, dayjs

sheetq faqat backend uchun (Node.js ≥ 24). U Google service account bilan autentifikatsiya qiladi va browserda chaqirilsa xato beradi.


Sozlash

1. Service account yarating

  1. Google Cloud Console da service account yarating va uning JSON kalitini yuklab oling.
  2. Loyiha uchun Google Sheets API ni yoqing.
  3. Jadvalni service account email'i ([email protected]) bilan ulashing — faqat o'qish uchun "Viewer", yozish uchun "Editor".

2. Spreadsheet klientini yarating

import Spreadsheet from "sheetq";
import cred from "./service-account.json" assert { type: "json" };

// To'liq credentials JSON obyektini berasiz:
const spreadsheet = new Spreadsheet(cred);                // o'qish + yozish

// Faqat o'qish rejimi (readonly scope so'raydi):
const ro = new Spreadsheet(cred, { readonly: true });

// Yoki email + key'ni to'g'ridan-to'g'ri (mas. env'dan):
const ss = new Spreadsheet(
  process.env.GOOGLE_CLIENT_EMAIL!,
  process.env.GOOGLE_PRIVATE_KEY!, // `\n` belgilar avtomatik to'g'rilanadi
);

Bitta Spreadsheet klienti ko'plab hujjatlarga xizmat qila oladi — auth va Sheets klienti lazy (kerak bo'lganda) quriladi va keshlanadi.

3. Hujjat ochib, modellar tavsiflang

const doc = spreadsheet.doc("<spreadsheetId>"); // jadval URL'idagi uzun id

const Users = doc.model("users", {       // "users" = varaq (tab) nomi
  columns: {
    id:    { col: "B", type: "number" },
    name:  { col: "C", type: "string" },
    email: { col: "D", type: "string" },
    role:  { col: "F", type: "string" },
  },
  startRow: 2, // ma'lumot boshlanadigan qator (header'dan keyin). Default: 2
});

Ustun ta'rifi (ColumnDef)

interface ColumnDef {
  col: string;                          // "A", "B", "C"... (ustun harfi)
  type?: "string" | "number" | "boolean" | "date" | `date:${string}`;
  transform?: (raw: Cell) => unknown;   // O'QISHda: xom katak → istalgan qiymat
  serialize?: (value: any) => Cell;     // YOZISHda: boy qiymat → katak qiymati
}

Tiplar va konvertatsiya

| type | O'qishda qaytadi | Yozishda qabul qiladi | | ------------------- | ----------------- | ---------------------- | | "string" | string \| null | string | | "number" | number \| null | number | | "boolean" | boolean \| null | boolean | | "date" | Date \| null | Date | | "date:DD.MM.YYYY" | Date \| null | formatlangan matn |

"date" — ISO sanani parse/format qiladi. Agar sana varaqda matn ko'rinishida saqlansa, "date:FORMAT" ishlating (FORMAT — dayjs formati), masalan: "date:DD.MM.YYYY HH:mm:ss".

transform va serialize — o'z konverteringiz

Tayyor type yetarli bo'lmaganda, o'z funksiyalaringizni berasiz. Eng muhimi — ular ikki xil yo'nalishda ishlaydi:

  • transformO'QISHda ishlaydi: varaqdan kelgan xom katakni (raw) istalgan qiymatga aylantiradi. Field'ning TS tipi aynan shu funksiyaning return tipidan olinadi.
  • serializeYOZISHda ishlaydi: sizning boy qiymatingizni varaqqa yoziladigan oddiy qiymatga (matn/son/boolean) aylantiradi.

Aynan siz belgilab ko'rsatgan misol:

import { getFormattedDate } from "utilzify";

const usersModel = taklifnomaDoc.model("users", {
  columns: {
    timestamp: {
      col: "A",
      type: "string",
      transform: raw => new Date(raw as string), // O'QISH:  "2026-06-22 14:30" → Date obyekti
      serialize: raw => getFormattedDate(raw),     // YOZISH:  Date → "22.06.2026 14:30" matni
    },
    id:    { col: "B", type: "number" },
    name:  { col: "C", type: "string" },
    email: { col: "D", type: "string" },
    role:  { col: "F", type: "string" },
  },
  startRow: 2,
});

Bu nima qiladi:

  • transform: raw => new Date(raw as string) — varaqdagi katakda sana matn ko'rinishida turadi. O'qiyotganda sheetq xom matnni oladi va uni JS Date obyektiga aylantiradi. Shuning uchun users[i].timestamp — bu haqiqiy Date, matn emas.
  • serialize: raw => getFormattedDate(raw) — yozayotganda (insert / update / set) siz Date (yoki boshqa qiymat) berasiz, lekin varaqqa chiroyli, formatlangan matn yozilishi kerak. getFormattedDate aynan shuni qiladi.

getFormattedDate — shunchaki misol formatlovchi funksiya (utilzify paketidan). O'rniga istalgan funksiya ishlatsa bo'ladi, masalan dayjs(raw).format("DD.MM.YYYY"). Yoki butun transform/serialize o'rniga shunchaki type: "date:DD.MM.YYYY HH:mm:ss" berib, xuddi shu natijaga erishish mumkin.

Ikki xil yondashuv: obyekt literali yoki columns builder

Ustunni ikki xil yozish mumkin — natija (tip ham, xulq ham) bir xil. Ikkalasini bitta modelda aralashtirib ham ishlatsa bo'ladi.

1-usul — obyekt literali (to'liq moslashuvchan):

doc.model("users", {
  columns: {
    id:     { col: "B", type: "number" },
    name:   { col: "C", type: "string" },
    active: { col: "E", type: "boolean" },
    bornAt: { col: "F", type: "date:DD.MM.YYYY" },
  },
});

2-usul — columns builder (qisqaroq):

import { columns as c } from "sheetq";

doc.model("users", {
  columns: {
    id:     c.number("B"),
    name:   c.string("C"),
    active: c.boolean("E"),
    bornAt: c.date("F", "DD.MM.YYYY"),
  },
});

Builder'lar literal tipni saqlaydi, shuning uchun row tipi aynan bir xil chiqadi (id: number | null, bornAt: Date | null, ...). Hech qanday tip yo'qotilmaydi.

Har bir builder:

| Builder | Teng obyekt literali | Field tipi (o'qishda) | | ------------------------------- | --------------------------------------------------- | ---------------------- | | c.string("C") | { col: "C", type: "string" } | string \| null | | c.number("B") | { col: "B", type: "number" } | number \| null | | c.boolean("E") | { col: "E", type: "boolean" } | boolean \| null | | c.date("F") | { col: "F", type: "date" } | Date \| null (ISO) | | c.date("F", "DD.MM.YYYY") | { col: "F", type: "date:DD.MM.YYYY" } | Date \| null (matn) | | c.parsed("G", parse, serialize?) | { col: "G", transform: parse, serialize } | parse return tipi |

  • c.string(col) — matn ustuni. O'qishda String(...), yozishda matn.
  • c.number(col) — son ustuni. O'qishda Number(...) (parse bo'lmasa null), yozishda son.
  • c.boolean(col) — mantiqiy ustun. "TRUE"/"true"/truetrue, aks holda false.
  • c.date(col) — ISO sana. c.date(col, "FORMAT") — sana matn ko'rinishida saqlangan bo'lsa, dayjs formati bilan (mas. "DD.MM.YYYY HH:mm:ss").

c.parsed — eng moslashuvchani (transform + serialize ning qisqa shakli)

c.parsed — bu obyekt literalidagi transform (va ixtiyoriy serialize)ning qisqa yozuvi. Tayyor tiplar (string/number/date...) yetmaganda ishlatiladi.

c.parsed(col, parse, serialize?)
  • parse: (raw) => RO'QISHda ishlaydi: varaqdan kelgan xom katakni (raw) siz xohlagan qiymatga aylantiradi. Field'ning tipi (R) aynan shu funksiyaning return'idan olinadi. Masalan r => new Date(...) bo'lsa, field tipi Date bo'ladi.
  • serialize?: (value: R) => CellYOZISHda ishlaydi (ixtiyoriy): sizning boy qiymatingizni (R) varaqqa yoziladigan oddiy qiymatga (matn/son/boolean) aylantiradi. Bermasangiz — faqat o'qish uchun mo'ljallangan bo'ladi.
import dayjs from "dayjs";

// Faqat o'qish: xom matn → Date
joinedAt: c.parsed("G", r => new Date(r as string)),

// O'qish + yozish: Date → "22.06.2026" matni
bornAt: c.parsed(
  "F",
  r => new Date(r as string),          // O'QISH:  "22.06.2026" → Date
  d => dayjs(d).format("DD.MM.YYYY"),   // YOZISH:  Date → "22.06.2026"
),

Bu quyidagi obyekt literaliga to'liq teng:

bornAt: {
  col: "F",
  transform: r => new Date(r as string),
  serialize: d => dayjs(d).format("DD.MM.YYYY"),
},

Qachon qaysi biri? Oddiy tipli ustunlar uchun builder qisqaroq va tozaroq; transform/serialize mantiqi murakkab bo'lsa yoki bir nechta parametr kerak bo'lsa, obyekt literali ko'proq nazorat beradi. Ikkalasini bir modelda aralashtirsangiz ham bo'ladi.

Model tipini chiqarib olish

import type { InferModel } from "sheetq";

type User = InferModel<typeof Users>;
// { timestamp: Date; id: number | null; name: string | null; ... }

O'qish

// Hamma qator (butun sheet, obyektlarga parse qilingan)
const all = await Users.all();

// Default — "yumshoq" qidiruv: matn ustunlarida registrga befarq qism-matn
const alis = await Users.find({ name: "ali" });        // "Ali", "Alisher" ham topadi

// Aniq tenglik
const u = await Users.findOne({ id: 5042 }, { exact: true });

// Qo'shimcha parametrlar: limit / orderBy / dir / exact
const top = await Users.find(
  { role: "admin" },
  { orderBy: "id", dir: "desc", limit: 10, exact: true },
);

Qidiruv qoidalari

  • Default (exact: false): matn (string) fieldlar registrga befarq contains bilan, qolganlari = bilan solishtiriladi. Foydalanuvchi qidiruv maydonchasi uchun qulay.
  • exact: true: hamma field aniq tenglik (=) bilan tekshiriladi.
  • match obyektidagi bir nechta field AND bilan birlashtiriladi.
  • null — bo'sh kataklarga mos keladi (is null).

Xom (raw) va header o'qish

// Xom o'qish — sxemada YO'Q ustunlarni HAM oladi; Cell[][] qaytaradi (parse qilmaydi)
const grid = await Users.allRaw();                       // butun sheet
const slice = await Users.allRaw({ fromRow: 2, limit: 100 });

// Header qatorlari (startRow'dan yuqoridagilar), xom ko'rinishda
const headerRows = await Users.headers();                // string[][]

// Har bir field uchun header qiymatlari
const map = await Users.headerMap();
// → { id: ["ID"], name: ["Ism"], ... }

Yozish

Yozish uchun o'qish-yozish scope kerak ({ readonly: true } BERMANG) va service account jadvalga Editor huquqiga ega bo'lishi shart.

// Oxiriga bitta / bir nechta qator qo'shish
await Users.insert({ id: 1, name: "Ali", email: "[email protected]" });
await Users.insertMany([
  { id: 2, name: "Vali" },
  { id: 3, name: "Guli" },
]);

// Mos qatorlarni patch bilan yangilash — nechta qator o'zgargani qaytadi
const changed = await Users.update({ id: 5042 }, { name: "Vali" }, { exact: true });

// Mos qatorlarni o'chirish (qatorlar olib tashlanadi) — nechta o'chirilgani qaytadi
const removed = await Users.delete({ role: "guest" });

// Qiymatlarni belgilangan qatordan boshlab USTIGA yozish (yangi qator QO'SHMAYDI)
await Users.set([{ id: 1, name: "Ali" }, { id: 2, name: "Vali" }]); // startRow'dan
await Users.set(rows, { fromRow: 10 });                             // 10-qatordan

// Mos qatorlarning kataklarini bo'shatish (qator JOYIDA qoladi) — soni qaytadi
await Users.clear({ id: 5042 }, { exact: true });
await Users.clear({});                                  // hamma ma'lumot qatorini bo'shatadi

// Pozitsiya (diapazon) bo'yicha tozalash — match'siz
await Users.clearRange();                               // hamma ma'lumot
await Users.clearRange({ fromRow: 5, toRow: 20 });
await Users.clearRange({ fromCol: "B", toCol: "D" });

// Formulani pastga "tortish" (autoFill) — nisbiy ssilkalarni har qatorga moslaydi.
// Manba katakda formula bo'lmasa — hech narsa qilmaydi (false qaytaradi).
await Users.fillFormula({ col: "E", fromRow: 2, toRow: 5000 });

update / delete / clear xuddi find kabi match parametrlarini qabul qiladi (exact, limit).


Xatolarni ushlash

Har bir amal o'ralgan — xato SpreadsheetError ko'rinishida, qaysi metod va qaysi varaqda yuz bergani konteksti bilan chiqadi.

import { SpreadsheetError } from "sheetq";

try {
  await Users.insert({ id: 1 });
} catch (err) {
  if (err instanceof SpreadsheetError) {
    console.error(err.message);   // "sheetq.insert (sheet: users): ..."
    console.error(err.context);   // { method: "insert", sheet: "users", ... }
  }
}

API qisqacha ma'lumotnoma

new Spreadsheet(credentials, options?) · new Spreadsheet(email, key, options?)

  • credentials — service-account JSON ({ client_email, private_key, ... }).
  • options.readonly — readonly scope so'raydi (default: o'qish + yozish).
  • options.scopes — maxsus scope'lar (readonly'dan ustun turadi).
  • .doc(spreadsheetId)Document

Document

  • .model(sheetName, config)Modelconfig bu { columns, startRow? }.

Model

| Metod | Qaytaradi | Tavsif | | -------------------------------------- | ------------------------ | ----------------------------------------------- | | find(match?, options?) | T[] | Mos qatorlar (server tomonda gviz). | | findOne(match?, options?) | T \| null | Birinchi mos qator. | | all(options?) | T[] | Hamma qator. | | allRaw(options?) | Cell[][] | Xom jadval, sxemada yo'q ustunlar bilan. | | headers() | string[][] | Xom header qatorlari (startRow'dan yuqori). | | headerMap() | Record<field,string[]> | Field bo'yicha header qiymatlari. | | insert(obj) | void | Bitta qator qo'shadi (oxiriga). | | insertMany(objs) | void | Ko'p qatorni bitta so'rovda qo'shadi. | | update(match, patch, options?) | number | Mos qatorlarni yangilaydi; soni qaytadi. | | delete(match, options?) | number | Mos qatorlarni o'chiradi; soni qaytadi. | | set(rows, options?) | void | Qatordan boshlab ustiga yozadi (qo'shmaydi). | | clear(match, options?) | number | Mos qatorlar kataklarini bo'shatadi; soni. | | clearRange(options?) | void | Qator/ustun diapazonini bo'shatadi. | | fillFormula({ col, fromRow, toRow }) | boolean | Formulani pastga to'ldiradi; yo'q bo'lsa false.| | col(field) | string | Field uchun ustun harfi. | | range(endRow?) | string | Sxema uchun A1 diapazoni (mas. users!B2:F). | | parse(row) / serialize(obj) | T / Cell[] | Qo'lda qator ↔ obyekt konvertatsiyasi. |

Parametrlar (Options)

  • FindOptions{ limit?, orderBy?, dir?: "asc" | "desc", exact? }
  • MatchOptions{ exact?, limit? }
  • SetOptions{ fromRow? }
  • RawOptions{ fromRow?, toRow?, limit? }
  • ClearOptions{ fromRow?, toRow?, fromCol?, toCol? }

Ishlab chiqish (Development)

yarn install
yarn typecheck   # tsc --noEmit
yarn build       # tsup → dist/ (ESM + CJS + .d.ts)

Litsenziya

MIT © Suhrobbek Soatov