Reference / KV API for Bun and Node
KV API for Bun and Node
Every public class, function and type of the KV engine in @tinyshed/tinystore, generated from its source. The KV guide explains how to use them, and the Python, Go and Rust pages list the same API.
ValueType
type ValueType = 'string' | 'bytes' | 'int' | 'bigint' | 'float' | 'bool'How a bucket keeps a value that is not JSON, as the core keeps a Rust type, so that every language reads the others' rows:
'string' its UTF-8 bytes
'bytes' the bytes
'int' an integer a number holds exactly
'bigint' an integer past what a number holds
'float' its eight bytes, big-endian, every bit kept
'bool' 1 or 0BucketOptions
interface BucketOptions {
/** a key expires this long after it was written, unless its write gives its own */
ttl?: Duration | undefined
/** a key expires this long after it was last read or written; not beside ttl */
idle?: Duration | undefined
/** how values are kept when they are not JSON */
type?: ValueType | undefined
/**
* Keeps every value sealed with the store's encryption key, as a password or a
* token is kept: the file holds what nobody reads without the key. The
* store seals and opens, so this program never holds the key. A key is not
* sealed, so a secret is a value and never a key, and a name keeps whether
* it is encrypted.
*/
encrypted?: boolean | undefined
}WriteOptions
interface WriteOptions {
/** the key expires this long from now */
ttl?: Duration | undefined
/** the key expires at this time */
expiresAt?: Time | undefined
/** writes only while the key is at this version: ConflictError otherwise */
ifVersion?: string
}Entry
interface Entry<T> {
key: string
value: T
/** compared only for equality: give it back to ifVersion */
version: string
/** undefined for a key that never expires */
expiresAt: Date | undefined
}Page
interface Page<T> {
entries: Entry<T>[]
next: string | undefined
}A page of a branch's keys, and where the next starts when there is one.
CountersOptions
interface CountersOptions {
/** a counter lasts this long from its first add */
ttl?: Duration | undefined
/**
* keeps counts in memory and writes them this often, so that an add costs
* no commit and a crash loses at most this span: attempts and hits, not money
*/
flushEvery?: Duration | undefined
}Bucket
class Bucket<T> {
readonly name: string
}A bucket's values under one branch of owners, the bucket's own when there are none.
Bucket.under
under(...owners: Key[]): Bucket<T>The branch the owners name below this one: whose keys these are.
Bucket.get
get(key: Key): Promise<T | undefined>The key's value, undefined when it holds none.
Bucket.entry
entry(key: Key): Promise<Entry<T> | undefined>The key's value with its version and expiry, undefined when it holds none.
Bucket.has
has(key: Key): Promise<boolean>Bucket.set
set(key: Key, value: T, options?: WriteOptions): Promise<void>Writes the value; a key that exists keeps its expiry unless the options give one.
Bucket.create
create(key: Key, value: T, options?: Omit<WriteOptions, 'ifVersion'>): Promise<boolean>Writes the value only where no live key is, and says whether it did.
Bucket.add
add(key: Key, options?: Omit<WriteOptions, 'ifVersion'>): Promise<boolean>Puts the key in a bucket used as a set; false when it was there.
Bucket.take
take(key: Key, options?: Pick<WriteOptions, 'ifVersion'>): Promise<T | undefined>Reads the value and removes the key in one commit: of two callers taking a one-time code at once, one gets it.
Bucket.delete
delete(key: Key, options?: Pick<WriteOptions, 'ifVersion'>): Promise<boolean>Removes the key, and says whether it was there.
Bucket.expire
expire(key: Key, when: Duration | Date, options?: Pick<WriteOptions, 'ifVersion'>): Promise<boolean>Gives a live key a new expiry, a span from now or a time, keeping its value; false when it holds none.
Bucket.clear
clear(): Promise<void>Removes this branch's keys and every branch under it, at once however many.
Bucket.list
list(options?: {
limit?: number | undefined
after?: Key | undefined
}): Promise<Page<T>>One page of this branch's own keys, in the byte order of their text: "10" before "9".
Bucket.all
all(options?: {
limit?: number | undefined
}): AsyncGenerator<Entry<T>>Walks this branch's own keys a page at a time, holding nothing between pages: a key written during the walk may or may not be met. It renews no idle key.
Counters
class Counters {
readonly name: string
}Numbers by key that only add up: 0 for one never added to or expired.
Counters.under
under(...owners: Key[]): CountersCounters.add
add(key: Key, n?: number | bigint): Promise<number>Adds n, 1 unless given, and gives the new count.
Counters.get
get(key: Key): Promise<number>Counters.delete
delete(key: Key): Promise<boolean>Removes the counter, and says whether it was there.
Counters.clear
clear(): Promise<void>Removes this branch's counters and every branch's under it.
Rate
type Rate = `${number}/${Unit}` | `${number}/${number}${Unit}`How many pass a span: '100/s', '5/10s', '1000/h', '300/7d', a span of one unit leaving out its 1. The type checks it as it is written.
RateLimitOptions
interface RateLimitOptions {
/** how many requests a key passes a span */
rate: Rate
/** how many may pass at once before the rate holds the next; the rate's count when absent */
burst?: number | undefined
}WindowUse
interface WindowUse {
used: number
limit: number
left: number
/** when the window resets; undefined for one not started */
resetsAt: Date | undefined
}One window of a quota as a key stands in it.
Allowance
interface Allowance<W extends string = never> {
ok: boolean
/** how many more would pass now */
left: number
/** when a refused request may try again; undefined when it passed */
retryAt: Date | undefined
/** a quota's windows by name; none for a rate limit */
windows: Record<W, WindowUse>
}What a rate limit or a quota answers a request.
RateLimit
class RateLimit {
readonly name: string
}A rate of requests a key: open it with store.rateLimit(name, { rate }).
RateLimit.under
under(...owners: Key[]): RateLimitThe rate limit of a branch, each key apart from the same key elsewhere.
RateLimit.allow
allow(key: Key, n?: number): Promise<Allowance>Asks for n requests of key, one unless given: all pass or none; more than the burst is InvalidError.
RateLimit.peek
peek(key: Key): Promise<Allowance>The answer one request would get, using nothing.
RateLimit.reset
reset(key: Key): Promise<void>Forgets key, so that its next request starts afresh.
Quota
class Quota<W extends string = string> {
readonly name: string
}Uses by key in named windows: open it with store.quota(name, { daily: '100/1d' }).
Quota.under
under(...owners: Key[]): Quota<W>Quota.allow
allow(key: Key, n?: number): Promise<Allowance<W>>Uses n of key's every window, one unless given, or none when one has no room; more than a window's limit is InvalidError.
const { ok, retryAt, windows } = await ai.allow(user.id)Quota.peek
peek(key: Key): Promise<Allowance<W>>Quota.reset
reset(key: Key): Promise<void>Quota.refund
refund(key: Key, n?: number): Promise<void>Gives n uses back to every window of key, one unless given.
OnceOptions
interface OnceOptions {
/** how long an answer is kept: a day when absent */
keep?: Duration | undefined
}Once
class Once<T> {
readonly name: string
}Answers kept once a key: open them with store.once(name).
Once.under
under(...owners: Key[]): Once<T>The answers of a branch, each key apart from the same key elsewhere.
Once.run
run(key: Key, fn: () => T | Promise<T>): Promise<T>The answer kept for key, or fn's, which is kept. A run of a key that another runs, here or in another process, waits for it and gets its answer; a throw of fn keeps nothing and is thrown, so the next run runs fn again. A connection lost after fn returned and before its answer was kept is OutcomeUnknownError.
const receipt = await charges.run(requestId, () => pay.charge(order, requestId))Once.get
get(key: Key): Promise<T | undefined>The answer kept for key, undefined when none is.
Once.delete
delete(key: Key): Promise<boolean>Forgets the answer kept for key, so that its next run runs fn again.
Tx
class Tx {}The calls of a transaction, each through tx.with(handle).
Tx.with
with<T>(bucket: Bucket<T>): TxBucket<T>
with(counters: Counters): TxCounters
with<T>(queue: Queue<T>): StoreTxQueue<T>
with<T>(channel: Channel<T>): TxChannel<T>Tx.read
read(parts: Parts, key: Key): Promise<Found>What the key holds as this transaction sees it: its own writes over what the store held.
Tx.write
write(method: Method, parts: Parts, fields: CallFields, found?: Found): voidKeeps a write for the commit; found is what a read of its key answers after it.
Tx.clear
clear(parts: Parts): voidTx.commit
commit(): Promise<void>Commits the writes, then hands over the messages published inside.
TxBucket
class TxBucket<T> {}A bucket's calls inside a transaction: reads answer at once, writes wait for its commit.
TxBucket.under
under(...owners: Key[]): TxBucket<T>TxBucket.get
get(key: Key): Promise<T | undefined>TxBucket.entry
entry(key: Key): Promise<Entry<T> | undefined>TxBucket.has
has(key: Key): Promise<boolean>TxBucket.set
set(key: Key, value: T, options?: WriteOptions): Promise<void>TxBucket.create
create(key: Key, value: T, options?: Omit<WriteOptions, 'ifVersion'>): Promise<boolean>Writes the value only where no live key is, and says whether it will.
TxBucket.add
add(key: Key, options?: Omit<WriteOptions, 'ifVersion'>): Promise<boolean>TxBucket.take
take(key: Key, options?: Pick<WriteOptions, 'ifVersion'>): Promise<T | undefined>TxBucket.delete
delete(key: Key, options?: Pick<WriteOptions, 'ifVersion'>): Promise<boolean>TxBucket.expire
expire(key: Key, when: Duration | Date, options?: Pick<WriteOptions, 'ifVersion'>): Promise<boolean>TxBucket.clear
clear(): Promise<void>Removes this branch's keys and every branch's under it, with the commit.
TxCounters
class TxCounters {}Counters inside a transaction: an add commits with its writes.
TxCounters.under
under(...owners: Key[]): TxCountersTxCounters.add
add(key: Key, n?: number | bigint): Promise<void>Adds n, 1 unless given, with the commit; the count is known once it has.