Getting started
Set up nuska and make your first commit in minutes.
This guide walks you through creating an engine, making commits, branching, merging, and opening a pull request.
Minimal setup
import { NuskaEngine, MemoryDataSourceAdapter, MemoryVersionStore } from "@codecanon/nuska"
const engine = new NuskaEngine({
datasource: new MemoryDataSourceAdapter(),
store: new MemoryVersionStore(),
})
await engine.init()init() creates the default main branch. Call it once before using any other method.
Your first commit
Commits are lists of MutationOp objects — atomic set or delete operations on keys.
const commit = await engine.commit(
[
{ type: "set", key: "user:1", value: { name: "Alice", role: "admin" } },
{ type: "set", key: "user:2", value: { name: "Bob", role: "viewer" } },
],
"Add initial users",
"alice"
)
console.log(commit.id) // "abc123…"After the commit, both keys are available in the datasource and the branch HEAD points to the new commit.
Branching
Create a branch at the current HEAD, then check it out to switch to it.
await engine.branch("feature/dark-mode")
await engine.checkout("feature/dark-mode")Changes committed while on feature/dark-mode are isolated from main.
Viewing history
const commits = await engine.log() // defaults to current branch
for (const c of commits) {
console.log(c.id, c.message, c.author, new Date(c.timestamp))
}Pass a branch name to inspect another branch:
const mainLog = await engine.log("main", { limit: 10 })Diffing
Compare any two commits to see what changed:
const diff = await engine.diff(commits[1].id, commits[0].id)
for (const entry of diff.entries) {
console.log(entry.type, entry.key, entry.oldValue, "→", entry.newValue)
}diff.entries contains objects with type: "added" | "removed" | "modified".
Merging branches
// On main, merge the feature branch
await engine.checkout("main")
const result = await engine.merge("feature/dark-mode")
if (result.status === "success") {
console.log("Merged:", result.commitId)
} else {
console.log("Conflicts:", result.conflicts)
}Resolving conflicts
When merge() returns { status: "conflict" }, supply a resolution for every conflicted key:
await engine.resolveConflicts("feature/dark-mode", [
{ key: "settings:theme", value: "dark" }, // keep a value
{ key: "settings:font", deleted: true }, // delete the key
])Pull requests
const pr = await engine.createPR("Add dark mode", "feature/dark-mode", "main")
console.log(pr.id, pr.status) // "open"
const mergeResult = await engine.mergePR(pr.id)
// The source branch is deleted automatically (pass { deleteBranch: false } to keep it)Reverting a commit
const revert = await engine.revertCommit(commit.id, {
message: "Revert: Add initial users",
author: "alice",
})This creates a new commit that undoes all operations from the target commit — it does not rewrite history.
Using browser persistence
Swap the in-memory adapters for IndexedDB ones to persist data across page reloads:
import {
IndexedDBDataSourceAdapter,
IndexedDBVersionStore,
} from "@codecanon/nuska/adapters"
const engine = new NuskaEngine({
datasource: new IndexedDBDataSourceAdapter("my-app-data"),
store: new IndexedDBVersionStore("my-app-versions"),
})
await engine.init()React
For React projects use the useNuskaEngine hook — see React bindings for the full guide.
import { useNuskaEngine } from "@codecanon/nuska/react"
import { MemoryDataSourceAdapter, MemoryVersionStore } from "@codecanon/nuska"
const datasource = new MemoryDataSourceAdapter()
const store = new MemoryVersionStore()
export function App() {
const { ready, currentBranch, commit, branch, checkout, log } =
useNuskaEngine({ datasource, store })
if (!ready) return <p>Initializing…</p>
return <p>On branch: {currentBranch}</p>
}