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

@motiontr/motiondb

v1.0.1

Published

Discord botları için hızlı, atomik ve kullanımı kolay JSON veritabanı (SQLite yok, tek dosya)

Readme

motiondb

Discord botları için tek dosyalık, atomik ve hızlı JSON veritabanı. discord.js ile birlikte kullanılmak üzere tasarlandı, ama framework'ten bağımsızdır.

{
  "guilds": {
    "123456789012345678": { "modLog": "918273645091827364", "welcome": { "channelId": "987654321098765432" } }
  },
  "users": {
    "555555555555555555": { "coins": 250, "inventory": ["ticket", "vip"] }
  },
  "cooldowns": { "555555555555555555:daily": 1730000000000 }
}

Kurulum

npm install
npm run build        # TypeScript derleme -> dist/
npm test             # 49 test (23 çekirdek + 26 API)
npm run example      # examples/basic.js
npm run example:api  # examples/api.js (MotionDB API turu)

Kendi projenizde:

npm install @motiontr/motiondb
import { MotionDB, JSONDatabase } from '@motiontr/motiondb';

const db = new JSONDatabase({ path: './data/db.json' });

MotionDB API (asenkron, tablo tabanlı)

İkinci katman: tek JSON dosyasında tablolar, await'li ve dot-notation destekli arayüz. Discord botlarında kullanıcı/sunucu verisi için ideal:

import { MotionDB } from '@motiontr/motiondb';

const db = new MotionDB({ filePath: './data.json' });
await db.init();                                  // dosyayı yükler/oluşturur

// ---- okuma / yazma ----
await db.set('myKey', 'myValue');                 // → 'myValue'
await db.get('myKey');                            // → 'myValue'
await db.get('olmayan');                          // → null
await db.has('myKey');                            // → true
await db.delete('myKey');                         // → 1  (yoksa 0)
await db.ensure('ayarlar', { modLog: '918273645091827364' });    // yoksa oluşturur

// ---- dot notation: iç içe nesneler ----
await db.set('myUser.balance', 700);
await db.get('myUser.balance');                   // → 700
await db.get('myUser');                           // → { balance: 700 }

// ---- sayılar ----
await db.add('myUser.balance', 100);              // → 800
await db.sub('myUser.balance', 50);               // → 750
await db.increment('sayac', 1);                   // → add kısayolu
await db.decrement('sayac', 1);                   // → sub kısayolu

// ---- diziler ----
await db.set('myUser.items', ['Sword', 'Shield']);
await db.push('myUser.items', 'Armor');           // → [..., 'Armor']
await db.unshift('myUser.items', 'Helm');         // → başa ekler
await db.pull('myUser.items', 'Shield');          // → siler
await db.pull('myUser.items', (i) => i.length < 5, true); // once: ilk eşleşen
await db.pop('myUser.items');                     // → son eleman
await db.shift('myUser.items');                   // → ilk eleman

// ---- sorgular ----
await db.all();                                   // → [{ id, value }, ...]
await db.startsWith('userInfo_');                 // → [{ id, value }, ...]
await db.startsWith('x', 'bazıSatırListesi');     // → ikinci parametre anahtar
await db.count();                                 // → satır sayısı

// ---- tablolar (aynı dosyada ayrı bölüm) ----
const guilds = db.table('guilds');
await guilds.set('1552239063984246784.modLog', '918273645091827364');
await db.deleteAll();                             // → silinen satır sayısı
db.useNormalKeys(true);                           // nokta literal anahtar yapsın

Seçenekler

new MotionDB({
  filePath: './data.json', // dosya yolu (varsayılan './data.json')
  table: 'json',           // kök bölüm adı (varsayılan 'json')
  normalKeys: false,       // true ise nokta ayracı değil, literal anahtar
  deferWrites: false,      // true ise diske yazmayı erteler → flush() gerekir
  spaces: 2,               // JSON girintisi
  onCorrupt: 'throw',      // 'throw' | 'reset'
});

Metot özeti

| Kategori | Metot | Dönüş | | --- | --- | --- | | Okuma | get(key) | değer veya null | | | has(key) | boolean | | | all() | { id, value }[] | | | startsWith(query, key?) | { id, value }[] | | | count() | number | | Yazma | set(key, value) | yazılan değer | | | ensure(key, varsayılan) | değer | | | delete(key) | 1 / 0 | | | deleteAll() | silinen satır sayısı | | Sayı | add, sub, increment, decrement | yeni sayı | | Dizi | push, unshift, pop, shift, pull | dizi / eleman | | Tablo | table(name) | yeni MotionDB | | | createSingleton(options) | tek instance | | | useNormalKeys(bool) | void | | Kalıcılık | init(), flush(), flushSync() | — | | Olay | on('save' \| 'load' \| 'change' \| 'error', fn) | this |

Hata mesajları (geçersiz kullanımda):

  • First argument (key) needs to be a string
  • Missing second argument (value) (set(key, null) gibi)
  • Current value with key: (x) is not an array (push ile dizi olmayana)
  • Current value with key: (x) is not a number... (add ile sayı olmayana)

Dosya biçimi

{
  "json":   { "myKey": "myValue", "myUser": { "balance": 750 } },
  "guilds": { "1552239063984246784": { "modLog": "918273645091827364" } }
}

Aynı dosyayı paylaşan tüm MotionDB instance'ları (table() dahil) tek motor kullanır; birbirinin yazdığını silmezler. deferWrites: true ile her işlemde diske yazmayı erteleyip toplu işlerde await db.flush() çağırabilirsiniz.


Hızlı başlangıç

const db = new JSONDatabase({ path: './data/db.json' });
db.registerShutdownHooks(); // kapanışta bekleyen yazışları diske atar

// oku / yaz (dot-path)
db.set('guilds.123456789012345678.modLog', '918273645091827364');
db.get('guilds.123456789012345678.modLog');      // '918273645091827364'
db.get('guilds.olmayan.modLog', 'yok');           // 'yok'  (fallback)

// yoksa oluştur
db.ensure('users.555555555555555555', { coins: 0, inventory: [] });

// sayaç
db.increment('users.555555555555555555.coins', 250);

// dizi
db.push('guilds.123.roles.moderator', '111111111111111111');
db.pull('guilds.123.roles.moderator', '111111111111111111');

// sil / kontrol
db.has('users.555555555555555555');   // true
db.delete('users.555555555555555555.oldKey');

Seçenekler

new JSONDatabase({
  path: './data/bot.json', // dosya yolu (varsayılan './db.json')
  spaces: 2,               // girinti sayısı (varsayılan 2)
  debounce: 50,            // değişiklikten sonra yazma gecikmesi ms (varsayılan 50)
  autosave: true,          // false ise yalnızca save()/flushSync() yazar
  createIfMissing: true,   // dosya yoksa oluştur
  onCorrupt: 'throw',      // 'throw' | 'reset' (bozuk dosyayı yedekleyip sıfırlar)
  backupOnLoad: false,     // ilk yüklemde otomatik yedek al
  backupDir: './data/backups',
});

debounce: 0 yaparsanız her değişiklikte anında yazar. Varsayılan 50 ms, bir komut içindeki 50 yazımı tek dosya işlemine indirger.


API

Okuma

| Metod | Açıklama | | --- | --- | | get(path, fallback?) | Değeri okur, yoksa fallback döner | | has(path) | Yol var mı? | | all() | Kök nesnenin canlı referansı | | toJSON() | Kök nesnenin kopyası | | keys() | Kök kategoriler (['guilds', 'users', ...]) | | category(name, fallback?) | Bir kategorinin tamamı (db.category('guilds')) |

Yazma

| Metod | Açıklama | | --- | --- | | set(path, value) | Değer yazar | | ensure(path, defaultValue) | Yoksa oluşturur, varsa mevcut değeri döner | | delete(path) | Anahtarı siler, silindiyse true | | push(path, ...values) | Diziye ekler (dizi yoksa oluşturur) | | pull(path, value \| fn) | Diziden eşleşeni çıkarır (derin karşılaştırma) | | increment(path, amount?) | Sayısal değeri artırır, yeni değeri döner | | decrement(path, amount?) | Sayısal değeri azaltır | | edit(fn, path?) | Ham düzenleme — verinin tamamına erişip değiştirin | | clear() | Tüm veriyi siler |

Path sözdizimi

db.get('guilds.123.modLog');               // nokta ile
db.get('items[0].name');                  // dizi indeksi
db.get('users["a.b"].coins');             // nokta içeren anahtar
db.get(['users', 'a.b', 'coins']);        // segment dizisi (en hızlısı)

Not: Discord snowflake ID'leri Number'a çevrilmez (yok olurlardı), tüm segmentler string olarak tutulur.

Kalıcılık

| Metod | Açıklama | | --- | --- | | save() / flush() | Değişiklikleri atomik olarak yazar (Promise) | | flushSync() | Anında senkron yazar — çıkış öncesi çağırın | | load(force?) | Dosyayı okur (ilk çağrıda otomatik) | | reload() | Bellek değişimlerini atıp dosyayı yeniden okur | | registerShutdownHooks() | SIGINT/SIGTERM/exit → flushSync() |

Yedekleme

const file = db.backup('before-migration'); // path döner
db.listBackups();                           // yeniden eskiye tüm yedekler

Olaylar

db.on('change', (c) => console.log(c.type, c.path.join('.'))); // set | delete | clear | edit
db.on('save', (file) => console.log('yazıldı:', file));
db.on('load', (file) => console.log('yüklendi:', file));
db.on('corrupt', (backupPath) => console.log('bozuk dosya yedeklendi:', backupPath));
db.on('error', (err) => console.error(err)); // yazma hataları

discord.js örneği

import { Client, GatewayIntentBits, Events } from 'discord.js';
import { JSONDatabase } from '@motiontr/motiondb';

const db = new JSONDatabase({ path: './data/db.json', backupOnLoad: true });
db.registerShutdownHooks();

const client = new Client({
  intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent],
});

const prefix = '!'; // ön ek config.js'te durur, veritabanında değil

client.on(Events.MessageCreate, (message) => {
  if (message.author.bot || !message.guild) return;
  if (!message.content.startsWith(prefix)) return;

  const [cmd, ...args] = message.content.slice(prefix.length).trim().split(/\s+/);

  // !modlog #kanal — değişen sunucu ayarları veritabanında tutulur
  if (cmd === 'modlog') {
    const channel = message.mentions.channels.first();
    if (!channel) return void message.reply('Bir kanal etiketle: !modlog #kanal');
    db.set(`guilds.${message.guild.id}.modLog`, channel.id);
    message.reply(`Mod log kanalı ayarlandı: ${channel}`);
  }
});

Sınırlar ve iyi pratikler

  • Tek süreç içindir. Aynı dosyayı 2+ process (ör. shard cluster) aynı anda yazarsa son yazan kazanır. Sharding yapıyorsanız process başına ayrı dosya (data/shard-0.json) veya harici bir DB (SQLite/Redis) kullanın.
  • Orta ölçek için uygundur: birkaç MB'a kadar veri rahatça çalışır;
  • tüm veri bellekte tutulur, okuma O(1)'dir, yazma ise atomiktir (tmp dosya + rename, yarım kayıt olmaz).
  • Büyük veritabanlarda (on binlerce sunucu + yüksek write I/O) JSON yerine SQLite geçmeyi düşünün.
  • Process SIGKILL ile öldürülürse son debounce penceresindeki değişiklikler kaybolabilir; kritik işlemlerden sonra await db.flush() çağırın.

Proje yapısı

src/
  index.ts        dışa aktarımlar (MotionDB, JSONDatabase)
  MotionDB.ts     asenkron tablo API (get/set/push/pull/table/all/...)
  Database.ts     JSONDatabase çekirdeği (atomik yazma, yedek, olaylar)
  path.ts         dot-path çözümleyici ve okuma/yazma yardımcıları
examples/
  basic.js        çekirdek database özellikleri turu
  api.js          MotionDB API turu
tests/
  db.test.js        `npm test` — 23 test (çekirdek)
  motiondb.test.js  `npm test` — 26 test (API)