@tonk/keepsync
Version:
A reactive sync engine framework for use with Tonk apps
128 lines (126 loc) • 5.1 kB
TypeScript
import { StateCreator } from 'zustand';
import { Repo, DocHandle, DocumentId } from '@automerge/automerge-repo/slim';
import { DocNode, DirNode } from '../documents/addressing.js';
/**
* Configuration options for the sync middleware
*/
export interface SyncOptions {
/**
* Unique document ID for syncing this store
* This ID is used to identify the document in the sync engine and across peers
*/
docId: DocumentId;
/**
* Maximum time to wait for Repo initialization (in milliseconds)
* After this time, the initialization will time out and call onInitError
* @default 30000 (30 seconds)
*/
initTimeout?: number;
/**
* Callback function that is called when sync initialization fails
* This can be due to timeout or other errors during initialization
* @param error The error that occurred during initialization
*/
onInitError?: (error: Error) => void;
}
/**
* Middleware for syncing Zustand stores with Automerge
*
* This middleware creates a bidirectional sync between a Zustand store and an Automerge document:
* 1. Changes to the Zustand store are automatically synced to Automerge
* 2. Changes from Automerge (from other peers) are automatically applied to the Zustand store
*
* The sync is established when the Repo becomes available, which may not be immediate.
* The middleware handles the initialization timing and retries automatically.
*
* @example
* const useStore = create(
* sync(
* (set) => ({
* count: 0,
* increment: () => set(state => ({ count: state.count + 1 })),
* }),
* { docId: 'counter' }
* )
* );
*
* @param config - The Zustand state creator function that defines the store's state and actions
* @param options - Configuration options for the sync middleware
* @returns A wrapped state creator function that includes sync functionality
*/
export declare const sync: <T extends object>(config: StateCreator<T>, options: SyncOptions) => StateCreator<T>;
export declare const ls: (path: string) => Promise<DocNode | undefined>;
export declare const rm: (path: string) => Promise<boolean>;
export declare const mkDir: (path: string) => Promise<DirNode | undefined>;
/**
* Reads a document from keepsync
*
* This function retrieves a document at the specified path from the repository.
* 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
*/
export declare const readDoc: <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
*/
export declare const writeDoc: <T>(path: string, content: T) => Promise<void>;
export type DocChangeListener<T> = (payload: {
doc: T;
patches: any[];
patchInfo: any;
handle: DocHandle<T>;
}) => void;
/**
* Attaches a listener to a document at the specified path
*
* This function allows you to listen for changes to a document without using the full sync middleware.
* It returns a function that can be called to remove the listener when it's no longer needed.
* The listener receives detailed change information including patches and before/after state.
*
* @example
* // Attach a listener to a document with patch information
* const removeListener = await listenToDoc('my/document/path', (payload) => {
* const { doc, patches, patchInfo, handle } = payload;
*
* console.log('Document changed:', doc);
* console.log('Patches:', patches);
* console.log('Before:', patchInfo.before);
* console.log('After:', patchInfo.after);
* console.log('Source:', patchInfo.source);
*
* // Inspect individual patches for granular changes
* patches.forEach(patch => {
* console.log('Patch action:', patch.action);
* console.log('Patch path:', patch.path);
* console.log('Patch value:', patch.value);
* });
* });
*
* // Later, when you want to stop listening
* removeListener();
*
* @param path - The path identifying the document to listen to
* @param listener - The callback function that will be called when the document changes
* @returns Promise resolving to a function that removes the listener
* @throws Error if the SyncEngine is not properly initialized or the document doesn't exist
*/
export declare const listenToDoc: <T>(path: string, listener: DocChangeListener<T>) => Promise<() => void>;
declare global {
interface Window {
__REPO_REGISTRY__?: {
callbacks: ((repo: Repo) => void)[];
notifyCallbacks: () => void;
};
}
}