- KVReplicaSet: seed, apply, local scan, diff against the source, sync meta - KVOutbox: persisted offline queue with in-order replay (load() resumes it) - utils: KeyRange (also used for server authorization), KeyCounter, SerialQueue (also used by KVStorageHttp and the event-sourced store), RetryTimer (separate timers for sync and replay; previously one shared timer could cancel the other's retry) The composite now only orchestrates. Behaviour unchanged; new unit tests for the building blocks and the outbox. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qxbkp4tA7G5SCWjzWuxmE
37 lines
1.5 KiB
TypeScript
37 lines
1.5 KiB
TypeScript
import type { ScanOptions } from '../kv-storage/base-kv-storage.js'
|
|
import { Keys } from './keys.js'
|
|
|
|
/**
|
|
* A key and its whole subtree: `[prefix, prefix + '0')`. Escaping guarantees nothing else sorts
|
|
* in that range (not even lookalikes such as `prefix-x`). The empty prefix is every key.
|
|
*/
|
|
export class KeyRange {
|
|
/** Inclusive lower bound (`undefined` = unbounded). */
|
|
readonly gte: string | undefined
|
|
/** Exclusive upper bound (`undefined` = unbounded). */
|
|
readonly lt: string | undefined
|
|
|
|
constructor(readonly prefix: string) {
|
|
this.gte = prefix === '' ? undefined : prefix
|
|
this.lt = prefix === '' ? undefined : prefix + Keys.afterSeparator
|
|
}
|
|
|
|
contains(key: string): boolean {
|
|
return this.prefix === '' || key === this.prefix || key.startsWith(this.prefix + Keys.separator)
|
|
}
|
|
|
|
/** Whether a scan over `[gte, lt)` stays inside this range. */
|
|
covers(gte: string | undefined, lt: string | undefined): boolean {
|
|
if (this.prefix === '') return true
|
|
return gte !== undefined && lt !== undefined && gte >= this.gte! && lt <= this.lt!
|
|
}
|
|
|
|
/** Scan options narrowed to this range, or `undefined` if nothing is left. */
|
|
clamp(options: ScanOptions): ScanOptions | undefined {
|
|
const gte = this.gte !== undefined && (options.gte === undefined || options.gte < this.gte) ? this.gte : options.gte
|
|
const lt = this.lt !== undefined && (options.lt === undefined || options.lt > this.lt) ? this.lt : options.lt
|
|
if (gte !== undefined && lt !== undefined && gte >= lt) return undefined
|
|
return { ...options, gte, lt }
|
|
}
|
|
}
|