• Joined on 2026-01-27

sourcedb-client (0.5.4)

Published 2026-06-11 22:04:29 +00:00 by hibna

Installation

registry=https://gits.hibna.com.tr/api/packages/hibna/npm/
npm install sourcedb-client@0.5.4
"sourcedb-client": "0.5.4"

About this package

sourcedb-client

SourceDB'nin HTTP/JSON API'si için tipli, bağımlılıksız JavaScript/TypeScript istemcisi. Node 18+ (global fetch) ve modern tarayıcılarda çalışır. ESM ve CommonJS ikisi de desteklenir (0.4.0+).

Sunucuyu çalıştırın:

sourcedb --serve-http 127.0.0.1:7878 --db ./db

Kurulum

npm install sourcedb-client

Not: paket adı kayıt defterinde doluysa kendi kapsamınızla yayınlayın (@kullanici/sourcedb) ya da Gitea npm registry'sini kullanın.

// ESM
import { SourceDB } from "sourcedb-client";
// CommonJS (backend)
const { SourceDB } = require("sourcedb-client");

İstemci seçenekleri (0.4.0+)

const db = new SourceDB("http://127.0.0.1:7878", {
  token: "gizli",      // Authorization: Bearer <token> (sunucuda ayarlıysa)
  timeoutMs: 30000,    // istek başına zaman aşımı; 0 = kapalı (vars. 30sn)
  retries: 2,          // YALNIZ okuma uçlarında otomatik yeniden deneme (vars. 2)
  retryDelayMs: 200,   // denemeler arası taban bekleme; üstel artar (vars. 200ms)
  headers: {},         // her isteğe eklenecek ek başlıklar
  fetch: undefined,    // özel fetch (Node <18 / test ortamları)
});

Yeniden deneme yalnızca idempotent okuma uçlarında yapılır (get, mget, keys, find, search, records, recent, match, stats, health, metrics, tables, dicts…). Yazmalar (put, delete, append, add, createTable, dictIntern) asla otomatik tekrarlanmaz. Tekrarlanabilir hatalar: ağ hatası / zaman aşımı (status 0) ve HTTP 502/503/504.

Kullanım

import { SourceDB } from "sourcedb-client";

const db = new SourceDB("http://127.0.0.1:7878", { token: "gizli" }); // token isteğe bağlı

// Sunucu / gözlem
await db.health();                       // { status: "ok" }
await db.metrics();                      // sorgu/gecikme sayaçları
await db.resources();                    // { cpu_percent, rss_bytes, disk_bytes, ... }
await db.tables();                       // [{ name, engine }]
await db.createTable("users", "Collection");
await db.tableStats("users");            // "engine=Collection records=…"

// KeyValue (değerler string olarak saklanır)
const kv = db.kv("oturumlar");
await kv.put("sess:1", "token-abc");
await kv.get("sess:1");                  // "token-abc" | null
await kv.mget(["sess:1", "yok"]);        // { "sess:1": "token-abc", "yok": null }
// Değerler JSON ise sunucu tarafı alan projeksiyonu (yük küçülür):
await kv.mget(["8rypRidm"], { root: "_source", fields: ["home", "away"] });
await kv.keys({ limit: 100 });           // ["sess:1", ...]
await kv.delete("sess:1");

// Collection (tipli alanlar + ikincil/tam-metin indeks)
const c = db.collection("users");
await c.createIndex("email", { unique: true });
await c.createTextIndex("bio");
await c.put("user:1", { ad: "Ali Veli", email: "ali@x.com", yas: 30, aktif: true, bio: "rust sever" });
await c.get("user:1");                   // { ad, email, yas, aktif, bio } | null
await c.find({ email: "ali@x.com" });    // ["user:1"]
await c.search("bio", "rust", "and");    // ["user:1"]
await c.records({ limit: 50 });          // [{ pk, fields }]
await c.delete("user:1");

// Log (kullanıcı başına akış)
const log = db.log("bildirimler");
await log.append(7, "merhaba");
await log.recent(7, 20);                 // ["merhaba", ...] en yeni → eski

// Odds (oran eşleştirme)
const odds = db.odds("maclar");
await odds.add(1, [{ source: 0, market: "1X2", outcome: 0, value: 1.85 }]);
await odds.match([{ source: 0, market: "1X2", outcome: 0, value: 1.85 }], { tolerance: 0 });
await odds.stats();

// Docs (iç-içe JSON doküman + ES-uyumlu sorgu DSL'i + aggregations) — 0.5.0
await db.createTable("posts", "Docs");
const docs = db.docs("posts");           // TS: db.docs<Post>("posts")
await docs.put("p:1", { baslik: "merhaba", yazar: { ad: "ali" }, begeni: 0,
                        etiketler: ["rust", "db"], createdAt: "2026-06-10T10:00:00Z" });
await docs.get("p:1");                   // doküman | null
await docs.patch("p:1", { baslik: "yeni" }, {
  unset: ["yazar.ad"],                   // alan silme (dotted path)
  inc: { begeni: 1 },                    // ATOMİK sayaç (tablo kilidi altında)
  upsert: false,
});                                      // "created" | "updated" | null
await docs.mget(["p:1", "p:2"], { fields: ["baslik"] });
await docs.search({                      // ES-uyumlu DSL
  query: { bool: {
    must: [{ term: { "etiketler.keyword": "rust" } }],   // .keyword soyulur
    filter: [{ range: { createdAt: { gte: "now-7d" } } }],
  }},
  sort: [{ createdAt: { order: "desc" } }, "_id"],
  from: 0, size: 20,
  _source: ["baslik", "begeni"],
  aggs: { gunluk: { date_histogram: { field: "createdAt", calendar_interval: "day" },
                    aggs: { ort: { avg: { field: "begeni" } } } } },
});                                      // { total, hits: [{pk, doc, sort}], aggregations }
await docs.count({ term: { "yazar.ad": "ali" } });
await docs.updateWhere({ term: { arsiv: true } }, { inc: { puan: 1 }, unset: ["gecici"] });
await docs.deleteWhere({ range: { createdAt: { lt: "now-90d" } } });
await docs.bulk([{ op: "put", pk: "p:2", doc: { baslik: "iki" } },
                 { op: "patch", pk: "p:1", inc: { begeni: 1 } },
                 { op: "delete", pk: "p:9" }]);
await docs.fields();                     // alan keşfi: [{ path, docs, str, num, bool, indexed }]
await docs.setPolicy(["match_id", "meta.*"]); // dev tablolarda RAM koruması ("all"|"none"|[yollar])
// Büyük okumalar: search_after ile akış — 50K+ doküman RAM'de birikmez.
for await (const h of docs.scan({ query: { match_all: {} } }, { pageSize: 2000 })) {
  // h.pk, h.doc
}

// Sözlükler: lokal .dict kopyası ve import sırasına bağlı id'ler gerekmez.
await db.dicts();                                            // [{ name, bytes }]
await db.dictResolve("football_bookmakers", ["bookmaker_bet365"]); // { bookmaker_bet365: 4 }
await db.dictNames("football_matchids", [12, 34]);           // { "12": "8rypRidm", "34": ... }
await db.dictIntern("football_bookmakers", ["yeni_buro"]);   // YAZMA: yoksa ekler (tek-yazar kuralı!)
await db.dictEntries("football_bookmakers");                 // { total, entries:[{id,name}], next } — tüm bahisçiler
await db.dictEntriesAll("football_bookmakers");              // [{id,name}] — sayfaları otomatik birleştirir
// En pratiği: match'te çözümü doğrudan istemek — source bahisçi ADI olur,
// sonuç ES matchId'leriyle döner (matchKeys):
const { matches, matchKeys } = await db.odds("football_odds").match(
  [{ source: "bookmaker_bet365", market: "MATCH_AH_-0.25", value: 3.45 }],
  { sourceDict: "football_bookmakers", matchDict: "football_matchids" },
);

Elasticsearch drop-in adaptörü (es-adapter)

ES'e bulk/update/search ile yazan mevcut feed scriptlerini (ör. canlı oran çekme döngüleri) kod değişikliği olmadan SourceDB'ye taşımak için:

import { SourceDBEsAdapter } from "sourcedb-client/es-adapter";          // ESM
const { SourceDBEsAdapter } = require("sourcedb-client/es-adapter");     // CJS
const esClient = new SourceDBEsAdapter({
  node: "http://127.0.0.1:7878",
  token: null,
  rawEngine: "Docs",   // YENİ tabloların motoru; vars. "KeyValue" (eski uyum)
});

v2 (0.5.0): adaptör motor-farkındadır. ES index X → tablo es_X; tablo hangi motordaysa o yol kullanılır:

  • Docs tablosu → tam yüzey: search gövdesi sunucuya AYNEN gider (bool/term/terms/range+now/exists/prefix/wildcard/match + sort + _source + aggregations), update/bulk sunucu tarafı atomik merge-upsert (tek istek), gerçek scroll (search_after ile devam), get · index · delete · count · deleteByQuery · updateByQuery (painless script yerine doc/unset/inc uzantısı), indices.getMapping (alan keşfi — market discovery) · indices.exists/create/refresh · cat.indices (desen).
  • KeyValue tablosu (CLI --pull-es düzeni, 0.3/0.4 mirası) → eski davranış birebir: oku-birleştir-yaz upsert, yalnız terms araması, scroll tek sayfa.

bulk artık index/create (tam yazma), update (+doc_as_upsert) ve delete aksiyonlarını destekler; kalem hatası partiyi durdurmaz (errors: true + kalemde error).

Odds eşlemesi her iki motorda da aynı:

  • bookmaker_* / basket_bookmaker_* dokümanı yazılınca maçın TÜM bürolarının oranları ham tablolardan yeniden kurulup football_odds/basket_odds tablosuna tam-değiştirme ile yazılır → tek büronün kapanışı güncellenirken diğer bürolar korunur, eski değerler sorgudan düşer; yazılan ANINDA sorgulanabilir.
  • Kimlikler string gider (match_key + bahisçi adı); sunucu sözlüğe intern eder — istemci sayısal id bilmez, id'ler import sırasından bağımsız.

Tek-yazar kuralı: adaptör çalışırken aynı sözlüklere yazan CLI --pull-es aynı anda çalıştırılmamalıdır (iki yazar id çakıştırır). Aggs notu: aggregations yalnız TEK Docs tablosunu hedefleyen aramalarda desteklenir (desen birden çok tabloya açılırsa hata).

Hata yönetimi

Başarısız istekler SourceDBError fırlatır (adaptörde alt sınıfı SourceDBEsAdapterError). 0.4.0'dan itibaren ağ hatası ve zaman aşımı da aynı sınıfla sarılır (status === 0):

import { SourceDB, SourceDBError } from "sourcedb-client";
try {
  await db.collection("users").put("user:9", { email: "ali@x.com" }); // benzersiz ihlali
} catch (e) {
  if (e instanceof SourceDBError) {
    e.status;   // HTTP kodu; 0 = ağ hatası / zaman aşımı
    e.body;     // sunucunun hata gövdesi (JSON çözülmüşse nesne)
    e.method;   // "PUT"
    e.path;     // "/coll/users/records/user%3A9"
    e.cause;    // ağ hatasında alttaki fetch hatası
  }
}

TypeScript

Tipler üretilen bildirimlerde gömülü (dist/*.d.ts + CJS için *.d.cts); ek @types gerekmez.

import { SourceDB, type CollectionRecord } from "sourcedb-client";
const db = new SourceDB();
const recs: CollectionRecord[] = await db.collection("users").records();

Geliştirme

Kaynak src/ altında TypeScript'tir; dist/ tsup ile üretilir (ESM + CJS + d.ts):

npm install
npm run build         # dist/ üretir
npm run typecheck     # tsc --noEmit
npm run smoke         # canlı duman testi (çalışan sunucu ister; SDB_URL/SDB_TOKEN)
npm run smoke:docs    # docs<T>() yüzeyi canlı testi
npm run smoke:es      # es-adapter (KV mirası) canlı testi (taze db ile)
npm run smoke:es-docs # es-adapter v2 (rawEngine: Docs) canlı testi
npm run smoke:cjs     # CJS require doğrulaması (sunucu istemez)
npm run smoke:qol     # timeout/retry davranış testi (sunucu istemez)

Kökteki index.js / es-adapter.js yalnızca depo-içi göreli import'lar için duran shim'lerdir (dist'e yönlendirir); paket tüketicileri exports haritasıyla doğrudan dist'i alır.

Sürüm notları

  • 0.5.4bookmakerOdds(bookmaker, matchId, { prefix }): bir maçın tek bahisçideki oranlarını forward Docs tablosundan (es_bookmaker_<ad>) doğrudan pk araması ile getirir (µs/düşük-ms; odds motorunun ters taramasından ~yüzlerce kat hızlı). Bu tablolar CLI --materialize-bookmakers ile odds motorundan üretilir (ya da ingest loop'ları besler).
  • 0.5.3odds(t).oddsOf(matchId, { source | sourceName, sourceDict, matchDict }): bir maçın oranlarını geri okur, isteğe bağlı bahisçi süzgeciyle. Ters indeks maç→oran yolu tutmadığından süzgeçsiz okuma büyük depoda yavaştır; tek bahisçiye süzünce diğer kaynakların bitmap'leri okunmaz (ölçüm: ~15 sn → ~0.3 sn). Sunucuya GET /odds/<t>/match/<id>?source= eklendi.
  • 0.5.2dictEntries(name, {limit, after}) + dictEntriesAll(name): bir sözlüğün tüm girişlerini (id-sıralı, cursor sayfalı) listeler — "tüm bahisçileri getir" (/odds/bookmakers) için. Sunucuya GET /dicts/<ad>/entries eklendi (büyük sözlükler tek seferde dökülmez).
  • 0.5.1 — es-adapter: rawEngine: "Docs" ile YENİ oluşturulan bookmaker_*/basket_bookmaker_* ham tablolarına otomatik meta-indeks politikası (CLI --pull-es ile birebir) — yüzlerce market alanı değer-indekslenmez, alan keşfi çalışmaya devam eder.
  • 0.5.0Docs API: db.docs<T>() (put/get/patch[unset+atomik inc]/ delete/mget/search/count/deleteWhere/updateWhere/bulk/fields/list/setPolicy)
    • scan() async iterator (search_after akışı); tables(pattern); es-adapter v2: motor-farkında — Docs tablolarında tam DSL passthrough, aggregations, gerçek scroll, count/deleteByQuery/updateByQuery, get/index/delete, bulk index|create|update|delete, indices.getMapping/exists/create/refresh, cat.indices; rawEngine seçeneği (vars. "KeyValue" — mevcut dağıtım davranışı değişmez).
  • 0.4.0 — kaynak TypeScript'e taşındı; dual ESM + CommonJS çıktı; istek zaman aşımı (timeoutMs, vars. 30sn); idempotent okumalarda otomatik yeniden deneme (retries, vars. 2; 502/503/504 + ağ hatası); zengin hata (status/body/method/path/cause; ağ hataları da SourceDBError); dictIntern(); headers seçeneği. Telde giden API değişmedi.
  • 0.3.0es-adapter: @elastic/elasticsearch v8 drop-in adaptörü.
  • 0.2.0kv.mget (projeksiyonlu), sözlük uçları, odds.match sourceDict/matchDict.

Dependencies

Development Dependencies

ID Version
@types/node ^22.10.0
tsup ^8.3.5
typescript ^5.7.0

Keywords

sourcedb database client kv key-value fulltext odds log
Details
npm
2026-06-11 22:04:29 +00:00
4
MIT
latest
38 KiB
Assets (1)
Versions (5) View all
0.5.4 2026-06-11
0.5.2 2026-06-11
0.5.1 2026-06-11
0.3.0 2026-06-10
0.1.0 2026-06-03