diffable-objects
Version:
A package for dynamic state tracking for Cloudflare's Durable Objects using SQLite
53 lines (52 loc) • 1.8 kB
JavaScript
import { recursivelyObservable } from "./observable.js";
import { SqliteState } from "./sqlite.js";
import { unreachable } from "./util.js";
export * from "./decorator.js";
/**
* Dynamically create a state object that persists changes to durable storage using Proxy and SQLite.
*
* @example
* ```
* import { DurableObject } from "cloudflare:workers";
* import { state } from "diffable-objects";
*
* class Counter extends DurableObject {
* #state = state(this, "counter", { count: 0 });
*
* async fetch(request) {
* this.#state.count += 1;
* return new Response(`Count: ${this.#state.count}`);
* }
* }
* ```
*
* @param ctx the DurableObject state.
* @param name the name of the state, typically the name of the field.
* @param initialState the initial state of this object, this must be the same every time the DO is created.
* @param options options for configuring how the state is persisted.
* @returns a copy of state that will persist changes.
*/
export function state(ctx, name, initialState, options = {
snapshotPolicy: { changes: 10 },
}) {
const state = new SqliteState(name, ctx.storage);
const data = state.resume(initialState);
return recursivelyObservable(data, {
onUpdate(changes, data) {
state.appendChanges(changes);
maybeSnapshot(data, state, options.snapshotPolicy ?? { changes: 10 });
},
});
}
function maybeSnapshot(data, state, snapshotPolicy) {
if (snapshotPolicy === "every-change") {
state.snapshot(data);
}
else if (typeof snapshotPolicy === "object" &&
"changes" in snapshotPolicy) {
const latest = state.latestChange() ?? unreachable();
if (latest.id % snapshotPolicy.changes === 0) {
state.snapshot(data);
}
}
}