DO NOT EDIT - AUTO-GENERATED FROM docs/src/llms/shared/keepsync/worker-nodejs.md
This file is automatically generated from the documentation.
Edit the source file instead: docs/src/llms/shared/keepsync/worker-nodejs.md
Generated on: 2025-07-17T13:11:10.053Z
DO NOT EDIT - AUTO-GENERATED FROM docs/src/llms/shared/keepsync/worker-nodejs.md
This file is automatically generated from the documentation.
Edit the source file instead: docs/src/llms/shared/keepsync/worker-nodejs.md
Generated on: 2025-07-17T13:11:10.054Z
Basic Usage
1. Set Up the Sync Provider
Initialize the sync engine in your application entry point (or before using any synced stores):
// index.tsx
import { configureSyncEngine } from "@tonk/keepsync";
import { BrowserWebSocketClientAdapter } from "@automerge/automerge-repo-network-websocket";
import { NodeFSStorageAdapter } from "@automerge/automerge-repo-storage-nodefs";
const wsAdapter = new BrowserWebSocketClientAdapter("ws://localhost:7777/sync);
const storage = new NodeFSStorageAdapter();
configureSyncEngine({
url: "http://localhost:7777",
network: [wsAdapter as any],
storage,
});
2. Create a Synced Store with the Middleware
Use the sync
middleware to create stores that automatically synchronize with other clients:
// stores/counterStore.ts
import { createStore } from 'zustand/vanilla';
import { sync, DocumentId } from '@tonk/keepsync';
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
export const counterStore = createStore<CounterState>(
sync(
// The store implementation
(set) => ({
count: 0,
// Increment the counter
increment: () => {
set((state) => ({ count: state.count + 1 }));
},
// Decrement the counter
decrement: () => {
set((state) => ({ count: Math.max(0, state.count - 1) }));
},
// Reset the counter
reset: () => {
set({ count: 0 });
},
}),
// Sync configuration
{
docId: 'counter' as DocumentId,
// Optional: configure initialization timeout
initTimeout: 30000,
// Optional: handle initialization errors
onInitError: (error) => console.error('Sync initialization error:', error)
}
)
);
3. Manually fetch the state
Because this is a Node project, we need to use zustand in a different way as it is used in React components. Each time you want fresh state you will need to use the getState()
function.
const counterStore = createStore<CounterState>(
sync(
// The store implementation
(set) => ({
count: 0,
// Increment the counter
increment: () => {
set((state) => ({ count: state.count + 1 }));
},
// Decrement the counter
decrement: () => {
set((state) => ({ count: Math.max(0, state.count - 1) }));
},
// Reset the counter
reset: () => {
set({ count: 0 });
},
}),
// Sync configuration
{
docId: 'counter' as DocumentId,
// Optional: configure initialization timeout
initTimeout: 30000,
// Optional: handle initialization errors
onInitError: (error) => console.error('Sync initialization error:', error)
}
)
);
const state = counterStore.getState();
state.increment();
console.log(`The current count is: ${store.getState().count}`);
Directly reading and writing documents
You can also directly read and write documents and address them using paths similar to a filesystem. This is useful for when you need more fine-grained control over document access and a zustand store is too cumbersome (e.g. when you want each document to have its own space and be directly addressable);
import { readDoc, writeDoc, ls, mkDir, rm, listenToDoc } from "@tonk/keepsync";
/**
* Reads a document from keepsync
*
* This function retrieves a document at the specified path in your sync engine.
* It returns the document content if found, or undefined if the document doesn't exist.
*
* @param path - The path identifying the document to read
* @returns Promise resolving to the document content or undefined if not found
* @throws Error if the SyncEngine is not properly initialized
*/
readDoc = async <T>(path: string): Promise<T | undefined>;
/**
* Writes content to a document to keepsync
*
* This function creates or updates a document at the specified path.
* If the document doesn't exist, it creates a new one.
* If the document already exists, it updates it with the provided content.
*
* @param path - The path identifying the document to write
* @param content - The content to write to the document
* @throws Error if the SyncEngine is not properly initialized
*/
writeDoc = async <T>(path: string, content: T);
/**
* Lists documents at a specified path
*
* This function retrieves a list of documents at the specified directory path.
* It returns an array of document names found at that path.
*
* @param path - The directory path to list documents from
* @returns Promise resolving to an array of document names
* @throws Error if the SyncEngine is not properly initialized
*/
ls = async (path: string): Promise<string[]>;
/**
* Creates a directory at the specified path
*
* This function creates a new directory at the specified path.
* If the directory already exists, it does nothing.
*
* @param path - The path where the directory should be created
* @throws Error if the SyncEngine is not properly initialized
*/
mkDir = async (path: string): Promise<void>;
/**
* Removes a document or directory at the specified path
*
* This function deletes a document or directory at the specified path.
* If removing a directory, it will remove all documents within it.
*
* @param path - The path of the document or directory to remove
* @param recursive - Whether to recursively remove directories (default: false)
* @throws Error if the SyncEngine is not properly initialized
*/
rm = async (path: string, recursive?: boolean): Promise<void>;
/**
* Listens for changes to a document
*
* This function sets up a listener for changes to a document at the specified path.
* The callback will be called whenever the document changes with detailed patch information.
*
* @param path - The path of the document to listen to
* @param callback - Function to call when the document changes, receives payload with doc, patches, patchInfo, and handle
* @returns A function that can be called to stop listening
* @throws Error if the SyncEngine is not properly initialized
*/
listenToDoc = <T>(path: string, callback: (payload: { doc: T; patches: any[]; patchInfo: any; handle: DocHandle<T> }) => void): Promise<() => void>;
File System Operations Example
Here's an example of how to use the file system operations:
import { ls, mkDir, rm, readDoc, writeDoc, listenToDoc } from "@tonk/keepsync";
// Create a directory structure
await mkDir("/users");
// Write a document
await writeDoc("/users/user1", { name: "Alice", age: 30 });
await writeDoc("/users/user2", { name: "Bob", age: 25 });
// List documents in a directory
const users = await ls("/users");
console.log(users); // ["user1", "user2"]
// Read a document
const user1 = await readDoc<{ name: string, age: number }>("/users/user1");
console.log(user1); // { name: "Alice", age: 30 }
// Listen for changes to a document
const unsubscribe = await listenToDoc<{ name: string, age: number }>("/users/user1", (payload) => {
const { doc: user, patches, patchInfo, handle } = payload;
if (user) {
console.log(`User updated: ${user.name}, ${user.age}`);
console.log("Patches:", patches);
console.log("Patch info:", patchInfo);
}
});
// Update the document (will trigger the listener)
await writeDoc("/users/user1", { name: "Alice", age: 31 });
// Stop listening when done
unsubscribe();
// Remove a document
await rm("/users/user2");
// Remove a directory and all its contents
await rm("/users", true);