Başlarken
Bu sayfadaki kod docs/src/snippets/getting-started.test.ts dosyasından gelir ve bun run test ile çalıştırılır; sayfa çalışmayan kod gösteremez.
Kurulum
Bölüm başlığı “Kurulum”bun add pricemeter zod # ya da npm i / pnpm addzod zorunlu değil: boyut ve bağlam şemaları için herhangi bir Standard Schema kütüphanesi (zod, valibot, arktype) ya da hiç şema kullanılabilir. pricemeter’in çalışma zamanı bağımlılığı yoktur; ESM + CJS olarak gelir, Node ≥ 20, Bun ve Workers’ta çalışır.
1. Katalog, tarife, adapter
Bölüm başlığı “1. Katalog, tarife, adapter”import { buildMetering, type Rate } from "pricemeter";import { memoryAdapters } from "pricemeter/testing";import { z } from "zod";
// $0.02 per SMS; from the 10,000th message on, $0.015. Amounts are integer micro-units.const sms: Rate = { id: "sms-2026", model: "graduated", tiers: [ { from: 0, unitPriceMicroUsd: 20_000 }, { from: 10_000, unitPriceMicroUsd: 15_000 }, ],};
// getRate + commit kept in memory: prepaid, "acme" starts with $10.const mem = memoryAdapters({ rates: { "otp/sms": sms }, prepaid: true, balances: { acme: 10_000_000 } });
const metering = buildMetering() .context(z.object({ accountId: z.string() })) .refs(["otp_send"]) .meter("otp/sms", { country: z.string().length(2) }) .bind(mem); // same as .getRate(mem.getRate).commit(mem.commit)
const ctx = { accountId: "acme" };-
Tarife (
Rate) tüm kademeleri taşır. Bir gözlemin hangi kademeye düştüğünerate()karar verir; tutarlar tamsayı mikro birimdir (20_000= $0,02). -
memoryAdapters(pricemeter/testing) test ve örnekler için bellek içi birgetRate+commitverir:usedSoFar’ı kendi yazdığı satırlardan sayar,prepaid: trueile bakiye yetmezseInsufficientCreditfırlatır. -
buildMetering()tek bir zincirdir;build()adımı yoktur..bind(mem)katalogu bu adapter’larla bağlar; üretimde aynı yere kendigetRatevecommitfonksiyonlarını verirsin.
2. İlk observe
Bölüm başlığı “2. İlk observe”const r = await metering.observe("otp/sms", { country: "TR" }, 2, { type: "otp_send", id: "msg_1" }, ctx);// → { ok: true, charged: 40000, lines: [{ meter: "otp/sms", quantity: 2, amount: 40000, tierIndex: 0, … }] }
mem.store.available("acme"); // 9_960_000mem.store.usage.length; // 1 usage row (refId "otp_send:msg_1:otp/sms")mem.store.ledger.length; // 1 ledger row (a "charge" of 40000)observe sırayla şunları yapar: bağlamı ve boyutları doğrular, getRate’ten tarifeyi ve usedSoFar’ı alır, price() ile tutarı hesaplar, planı commit’e verir ve sonucu döner. Plan iki listedir: parayı hareket ettiren ledger satırları ve kullanımı kaydeden usage satırları.
3. Retry güvenlidir
Bölüm başlığı “3. Retry güvenlidir”// A retry with the same ref computes the same plan; commit sees (refType, refId) again and writes nothing.await metering.observe("otp/sms", { country: "TR" }, 2, { type: "otp_send", id: "msg_1" }, ctx);mem.store.available("acme"); // still 9_960_000Her satırın refId’si ref’ten türetilir (otp_send:msg_1:otp/sms). commit aynı (refType, refId)’yi ikinci kez gördüğünde hiçbir şey yazmaz. Kütüphane bunun retry olduğunu bilmez; sonuç yine charged: 40000 der — “bu çağrının fiyatı”, “yazılan tutar” değil (A99) — ama bakiye değişmez.
4. Hatalar değer olarak döner
Bölüm başlığı “4. Hatalar değer olarak döner”await metering.observe("otp/sms", { country: "TUR" }, 1, { type: "otp_send", id: "msg_2" }, ctx);// → { ok: false, reason: "invalid_dims", meter: "otp/sms", cause: [...] } (zod: length 2)
await metering.observe("otp/sms", { country: "TR" }, 1_000, { type: "otp_send", id: "msg_3" }, ctx);// → { ok: false, reason: "insufficient_credit", … } ($20 > $9.96; nothing written)
mem.store.setRate("otp/sms", undefined); // the price table no longer has this meterawait metering.observe("otp/sms", { country: "TR" }, 1, { type: "otp_send", id: "msg_4" }, ctx);// → { ok: false, reason: "no_price", meter: "otp/sms" }{ ok: false, reason } döndüğünde hiçbir şey yazılmamıştır. Bakiye kontrolü kütüphanede değil commit’in içindedir: yalnızca ledger “bakiye ≥ tutar” kontrolünü yazımla aynı atomik adımda yapabilir. Tüm nedenler: observe ve hold → Result.
5. Yazmadan planı görmek
Bölüm başlığı “5. Yazmadan planı görmek”const { result, plan } = await metering.plan.observe("otp/sms", { country: "TR" }, 3, { type: "otp_send", id: "msg_5" }, ctx);// result.charged → 60000// plan.ledger → [{ op: "charge", account: "acme", amount: 60000, refType: "otp_send", refId: "otp_send:msg_5:otp/sms", at }]// plan.usage → [{ account: "acme", meter: "otp/sms", dims: { country: "TR" }, quantity: 3, amount: 60000, rateId: "sms-2026", … }]metering.plan.* aynı imzalarla çalışır, commit çağırmaz ve { result, plan, lines } döner. Test yazarken ya da planı başka bir yerde (ör. bir Durable Object içinde) uygulamak istediğinde kullanılır.
- “Ne kadar / ne zaman” sınırı: neyin kütüphanede, neyin uygulamada olduğu.
- Katalog ve Tarife ve modeller.
- Adapter yazma: gerçek bir veritabanına
commit.