nanostores atoms, maps and computed stores: working versions (tested 2026-10-16)
Model derived state with nanostores using atom and map stores, a computed derived store, plus listen/subscribe and get; assert computed updates and that unsubscribe stops notifications offline with no DOM.
passed5 passed · 0 failed2026-10-16 02:18 UTC- Status
- passed
- Last passed
- 2026-10-16 02:18 UTC
- Tests
- 5 / 5
- Language
- TypeScript
- Runtime
- node v22.23.3
pinned versions3
nanostores@1.5.5@types/node@26.6.5tsx@4.23.15
run it
npm install --no-audit --no-fund --ignore-scripts=false npm test
test environment
node v22.23.3 linux 6.17.0-1022-azure x64 runner: github-actions 38839686104
The code
Exactly the files that passed. Your agent can pull them with get_recipe("nanostores-atom-computed") (install), or see them on GitHub.
package.json
{
"name": "nanostores-atom-computed",
"private": true,
"type": "module",
"scripts": {
"test": "node --import tsx --test test/*.test.ts"
},
"dependencies": {
"nanostores": "1.5.5"
},
"devDependencies": {
"@types/node": "26.6.5",
"tsx": "4.23.15"
}
}
src/store.ts
import { atom, computed, map } from "nanostores";
import type { MapStore, ReadableAtom, WritableAtom } from "nanostores";
export type LineItem = { sku: string; price: number; qty: number };
export type Cart = {
taxRate: WritableAtom<number>;
items: MapStore<Record<string, LineItem>>;
subtotal: ReadableAtom<number>;
total: ReadableAtom<number>;
};
export function createCart(initialTaxRate = 0.1): Cart {
const taxRate = atom(initialTaxRate);
const items = map<Record<string, LineItem>>({});
const subtotal = computed(items, (bySku) => {
let sum = 0;
for (const item of Object.values(bySku)) {
sum += item.price * item.qty;
}
return Math.round(sum * 100) / 100;
});
const total = computed([subtotal, taxRate], (sub, rate) =>
Math.round(sub * (1 + rate) * 100) / 100,
);
return { taxRate, items, subtotal, total };
}
test/store.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { atom, map } from "nanostores";
import { createCart } from "../src/store.ts";
test("atom get and set", () => {
const count = atom(0);
assert.equal(count.get(), 0);
count.set(7);
assert.equal(count.get(), 7);
});
test("map get and setKey", () => {
const user = map({ name: "Ada", age: 36 });
assert.equal(user.get().name, "Ada");
user.setKey("age", 37);
assert.equal(user.get().age, 37);
user.set({ name: "Grace", age: 45 });
assert.deepEqual(user.get(), { name: "Grace", age: 45 });
});
test("subscribe fires immediately and stops after unsubscribe", () => {
const count = atom(1);
const seen: number[] = [];
const off = count.subscribe((value) => seen.push(value));
assert.deepEqual(seen, [1]);
count.set(2);
assert.deepEqual(seen, [1, 2]);
off();
count.set(3);
assert.deepEqual(seen, [1, 2]);
assert.equal(count.get(), 3);
});
test("listen skips the initial value and stops after unsubscribe", () => {
const count = atom(0);
const seen: number[] = [];
const off = count.listen((value) => seen.push(value));
assert.deepEqual(seen, []);
count.set(1);
assert.deepEqual(seen, [1]);
off();
count.set(2);
assert.deepEqual(seen, [1]);
});
test("computed derives from atom and map stores and freezes when unsubscribed", () => {
const cart = createCart(0.1);
const seen: number[] = [];
const off = cart.total.listen((value) => seen.push(value));
cart.items.setKey("a", { sku: "a", price: 10, qty: 2 });
assert.equal(cart.subtotal.get(), 20);
assert.equal(cart.total.get(), 22);
cart.taxRate.set(0.2);
assert.equal(cart.subtotal.get(), 20);
assert.equal(cart.total.get(), 24);
assert.equal(seen[seen.length - 1], 24);
const notifications = seen.length;
off();
cart.items.setKey("b", { sku: "b", price: 5, qty: 1 });
cart.taxRate.set(0.5);
// Listeners stop, but the stores themselves keep updating.
assert.equal(seen.length, notifications);
assert.equal(cart.subtotal.get(), 25);
assert.equal(cart.total.get(), 37.5);
});
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.