UNPKG

@tonk/keepsync

Version:

A reactive sync engine framework for use with Tonk apps

128 lines (126 loc) 5.1 kB
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; }; } }