simple-state-model
v0.1.1
Published
Simple State Model — transport- and storage-agnostic core: config/state controllers, the RFC6902 mutation queue, and the provider interface
Readme
simple-state-model
Transport- and storage-agnostic core of the Simple State Model. It has no knowledge of Postgres, GraphQL, or the network — everything external plugs in through a provider.
Concepts
| Concept | What it is |
| --- | --- |
| ConfigController | Holds a value. Emits value and status. Set with setValue(). |
| StateController | A ConfigController whose value is changed with RFC6902 patches. Emits patch alongside value. |
| StateControllerMutationQueue | Tracks provisional vs. accepted values so a client can apply optimistic patches and roll them back. |
| ConfigModel / StateModel | Registry of named providers. load(providerID, options) returns a controller. |
| BaseConfigProvider / BaseStateProvider | The interface a storage backend implements: load(), has(), delete(). |
Install
npm install simple-state-modelUsage
import { StateModel } from "simple-state-model";
const stateModel = new StateModel({ logger: console });
// in-memory provider is registered as "inmemory" and is the default
const stateController = await stateModel.load(undefined, { id: "element-1" });
stateController.on("patch", (patch) => console.log("changed", patch));
stateController.patch([
{ op: "add", path: "/title", value: "Hello" }
]);
console.log(stateController.value); // { title: "Hello" }Controller modes
StateController runs in one of two modes:
AUTHORITATIVE(default) — patches are accepted the moment they are queued. Use this on a server, or anywhere this process owns the truth.CLIENT— patches stay provisional until something accepts them. Use this in a browser so a local edit shows immediately but can be rolled back if the server rejects it.
import { StateController } from "simple-state-model";
const stateController = new StateController({ mode: StateController.MODES.CLIENT });
const mutation = stateController.patch([{ op: "add", path: "/title", value: "Hello" }]);
mutation.accept(); // or mutation.reject({ error })Writing a provider
A provider maps a state ID to a controller instance and decides where the value lives.
import { BaseStateProvider } from "simple-state-model";
class MyStateProvider extends BaseStateProvider {
async load(options) {
const stateID = options.id;
// return a cached controller, or build one from your backing store
return new this.controllerClass({ id: stateID, value: await this.read(stateID) });
}
async has(options) { /* ... */ }
async delete(options) { /* ... */ }
}See simple-state-model-postgres for a full implementation.
Related packages
simple-state-model-postgres— Postgres-backed providersimple-state-model-graphql-api— GraphQL schema and resolverssimple-state-model-graphql-client— GraphQL client
