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

alestadb

v2.0.1

Published

Official AlestaDB driver: pooled, pipelining, with change streams

Readme

alestadb

AlestaDB için resmi Node.js driver'ı. Havuzlu bağlantı, pipelining, change stream ve yönetim API'si. Sıfır bağımlılık.

npm i alestadb

Paket public, erişim değil. Paketi herkes indirebilir; sunucuya bağlanmak için global IP allowlist'te olmanız, geçerli bir credential'ınız olması ve proje bazlı IP allowlist'ten geçmeniz gerekir. Paket yalnızca protokolü bilir.


Hızlı başlangıç

const { AlestaClient } = require('alestadb');

// tlsFingerprint zorunlu: sunucu kendi imzaladığı bir sertifika sunuyor, onu
// tanıyan tek şey bu özet. Sunucu başlarken ekrana yazar.
const client = await new AlestaClient(
  'alesta://panel:[email protected]:5445/proje' +
    '?tlsFingerprint=3f9a…'
).connect();

const users = client.collection('users');

await users.insertOne({ email: '[email protected]', role: 'owner' });

const admins = await users.find({ role: 'admin' })
  .sort({ createdAt: -1 })
  .limit(20)
  .toArray();

await client.close();

ESM de çalışır:

import { AlestaClient } from 'alestadb';

Connection string

alesta://credential:parola@host:port/proje?seçenek=değer

| Seçenek | Varsayılan | Anlamı | |---|---|---| | poolSize | 10 | Havuzdaki soket sayısı | | connectTimeoutMS | 10000 | | | socketTimeoutMS | 30000 | | | appName | — | Sunucunun audit log'unda ve metriklerinde görünür | | tlsFingerprint | — | Sunucu sertifikasının SHA-256'sı — zorunlu, aşağıya bakın | | tlsInsecure | false | Sunucuyu doğrulamadan bağlan; bilinçli bir vazgeçiş |

Parolayı URL-encode edin. Driver connection string'i hiçbir zaman olduğu gibi yazdırmaz; client.toString() ve hata mesajları maskelenmiş hâli döner.

tlsFingerprint neden zorunlu

AlestaDB kendi imzaladığı bir sertifika sunar. Böyle bir sertifikada sertifika zinciri hiçbir şey kanıtlamaz, dolayısıyla sunucunun gerçekten o sunucu olduğunu söyleyen tek şey parmak izidir. Verilmezse bağlantı şifrelidir ama kimliği doğrulanmamıştır: ağ yolundaki biri araya girip trafiği okuyabilir ve değiştirebilir.

Bu yüzden driver, parmak izi olmayan bir bağlantıyı açmak yerine ERR_TLS_UNPINNED ile durur. İki istisna var:

  • Loopback (127.0.0.1, localhost) parmak izi istemez. Araya girilecek bir ağ yolu yoktur; oraya erişebilen zaten makinenin içindedir.
  • tlsInsecure=true yazarsanız uyarısız bağlanır. Bunu yalnız ne yaptığınızı bilerek kullanın.

Parmak izini sunucu açılışta ekrana yazar.


Sorgular

// Operatörler
await users.find({ age: { $gte: 18, $lt: 65 } }).toArray();
await users.find({ role: { $in: ['admin', 'owner'] } }).toArray();
await users.find({ $or: [{ role: 'owner' }, { age: { $lt: 20 } }] }).toArray();
await users.find({ 'profile.city': 'Ankara' }).toArray();
await users.find({ email: { $regex: '^vaco', $options: 'i' } }).toArray();
await users.find({ deletedAt: { $exists: false } }).toArray();

// Şekillendirme
await users.find().sort({ age: -1 }).skip(10).limit(5).project({ email: 1, _id: 0 }).toArray();

// Akış — büyük sonuçlar belleğe sığmak zorunda değil
for await (const user of users.find({ role: 'user' })) {
  console.log(user.email);
}

for await döngüsünden break ile çıkarsanız cursor sunucuda kapatılır. Sunucu cursor'ları bir snapshot tuttuğu için bu önemli: terk edilen bir cursor zaman aşımına kadar disk alanı tutar.

Neden sayfalama doğru

Cursor açıldığında sunucu bir depolama snapshot'ı sabitler. Siz sayfaları gezerken araya giren yazmalar sonucu kaydırmaz — hiçbir doküman atlanmaz, hiçbiri iki kez gelmez.


Yazma

await users.insertOne({ email: '[email protected]' });
await users.insertMany([{ n: 1 }, { n: 2 }], { ordered: false });

await users.updateOne({ email: '[email protected]' }, { $set: { role: 'admin' }, $inc: { logins: 1 } });
await users.updateMany({ role: 'user' }, { $set: { active: true } });
await users.updateOne({ email: '[email protected]' }, { $set: { role: 'user' } }, { upsert: true });

await users.deleteOne({ email: '[email protected]' });
await users.deleteMany({ active: false });

Operatörler: $set $unset $inc $push $pull $addToSet. Noktalı yol desteklenir ($set: { 'profile.city': 'Ankara' }).

matchedCount ile modifiedCount ayrıdır: zaten o değerde olan bir doküman eşleşir ama değişmez.


Index'ler

await users.createIndex({ email: 1 }, { unique: true });
await users.createIndex({ guild: 1, createdAt: -1 });   // bileşik
await users.listIndexes();
await users.dropIndex('email_1');

// Planlayıcının ne yapacağını görün
console.log(await users.explain({ email: '[email protected]' }));
// { plan: 'IXSCAN', index: 'email_1', sortSatisfiedByPlan: true, inMemorySort: false }

plan: 'COLLSCAN' görüyorsanız sorgunuz koleksiyonu baştan sona tarıyor demektir.


Change stream

Panelin "veriler anlık düşsün" ihtiyacını karşılayan mekanizma. Polling yok.

const stream = users.watch({ filter: { role: 'admin' } });

for await (const change of stream) {
  console.log(change.operationType, change.fullDocument);
  saveResumeToken(change.resumeToken);
}

Bağlantı koparsa kaldığınız yerden devam edin:

const stream = users.watch({ resumeToken: loadResumeToken() });

Yavaş abone. Sunucu, yazmaları bir aboneyi beklemek için asla durdurmaz. Çok geride kalırsanız en eski olaylar düşürülür ve stream ERR_STREAM_LAG ile hata verir — sessizce eksik veri almazsınız. Son token'dan devam edip aradaki olayları oplog'dan alabilirsiniz.


Yönetim

admin scope'lu bir credential gerektirir.

await client.admin.createProject('nesyatest', { ipRules: ['203.0.113.0/24'] });
await client.admin.createCredential('discord_bot', 'nesyatest', 'readWrite', 'uzun-parola');
await client.admin.setIpRules('nesyatest', ['203.0.113.0/24', 'localhost']);

// Erişimi kaldırmak anında etkili — oturumlar da kapanır
await client.admin.revokeCredential('discord_bot');

console.log(await client.admin.metrics());
await client.admin.backup('/var/backups/alestadb/manual');

Mongoose benzeri kullanım

Önceki pakete göre yazılmış kod çalışmaya devam eder:

const { connectDefault, Schema, model } = require('alestadb');

await connectDefault('alesta://panel:parola@host:5445/proje?tlsFingerprint=3f9a…');

const userSchema = new Schema({
  email: { type: String, required: true, unique: true },
  role: { type: String, default: 'user' },
  createdAt: { type: Date, default: () => new Date() },
});

const User = model('users', userSchema);
await User.createIndexes();

await User.create({ email: '[email protected]' });
const owner = await User.findOne({ role: 'owner' });

Hata yönetimi

const { AlestaError } = require('alestadb');

try {
  await users.insertOne({ email: '[email protected]' });
} catch (error) {
  if (error.isDuplicateKey) { /* ... */ }
  if (error.isAuthError) { /* ... */ }
  if (error.retryable) await new Promise(r => setTimeout(r, error.retryAfterMs ?? 100));
  console.log(error.code);   // "ERR_DUPLICATE_KEY" gibi kararlı bir kod
}

Mesaj metnine değil error.code'a bakın; kodlar protokolün parçası ve sürümler arasında değişmez.


Bilmeye değer üç şey

Snowflake'ler korunur. 2^53'ten büyük tamsayılar BigInt olarak döner. Discord snowflake'leri tam bu aralıkta; Number olarak dönselerdi son haneleri yuvarlanır ve iki farklı kullanıcı eşit görünürdü.

await stats.insertOne({ user: 1234567890123456789n });
const row = await stats.findOne({ user: 1234567890123456789n });
typeof row.user;   // 'bigint'

İlk bağlantı yavaştır, sonrakiler değil. Kimlik doğrulama SCRAM ile yapılır ve parola KDF'i Argon2id'dir — kasıtlı olarak pahalı. Node'da yerleşik Argon2 olmadığı için paket kendi saf JS implementasyonunu taşır (bu yüzden hiçbir bağımlılığı yok). Türetme süreç başına bir kez yapılır; havuzdaki diğer soketler ve reconnect'ler sunucudan alınan session token'ı kullanır.

İstekler pipeline'lanır. await etmeden birden çok çağrı başlatabilirsiniz; hepsi aynı anda uçar ve yanıtlar requestId ile eşleşir.

await Promise.all(ids.map(id => users.findById(id)));

Test

npm run test:unit          # sunucu gerekmez
ALESTADB_TEST_URI='alesta://owner:[email protected]:5444/' npm test

Kripto katmanı OpenSSL'e karşı doğrulanır: BLAKE2b 1200'den fazla girdi uzunluğunda ve RFC 7693 vektöründe, Argon2id ise OpenSSL'in ARGON2ID KDF'ine karşı birden çok parametre setinde. Entegrasyon testleri gerçek sunucuya karşı koşar — iki bağımsız implementasyonun aynı protokolde anlaşması.

Lisans

MIT