### Install Example Dependencies for zenQuery Web App Source: https://github.com/sylphxltd/zen-query/blob/main/examples/web-app/README.md This command installs the necessary dependencies for the web-app example. It should be run from the `examples/web-app` directory after installing root dependencies. ```bash pnpm install ``` -------------------------------- ### Run zenQuery Frontend Development Server Source: https://github.com/sylphxltd/zen-query/blob/main/examples/web-app/README.md This command starts the Vite development server for the frontend application. It's usually accessible at `http://localhost:5173` and allows interaction with the example application. ```bash pnpm run dev ``` -------------------------------- ### Install Example Dependencies for VSCode Extension Source: https://github.com/sylphxltd/zen-query/blob/main/examples/vscode-extension/README.md Installs the necessary project dependencies for the VSCode extension example. This command should be run from the `examples/vscode-extension` directory after the root monorepo dependencies are installed. ```bash pnpm install ``` -------------------------------- ### Install zenQuery Client and Dependencies Source: https://github.com/sylphxltd/zen-query/blob/main/packages/client/README.md Instructions to install the `@sylphlab/zen-query-client` package, a transport package (e.g., `websocket`), and `nanostores` for state integration using pnpm. ```bash pnpm add @sylphlab/zen-query-client # You also need to install a transport package, e.g.: pnpm add @sylphlab/zen-query-transport-websocket # And Nanostores for state integration: pnpm add nanostores @nanostores/react # or other framework bindings ``` -------------------------------- ### ZenQuery Monorepo Development Commands Source: https://github.com/sylphxltd/zen-query/blob/main/README.md This section provides essential 'pnpm' and 'Turborepo' commands for developing within the ZenQuery monorepo, covering dependency installation, building all packages, running tests, linting, formatting, and a comprehensive validation step. ```bash pnpm install pnpm run build pnpm run test pnpm run lint pnpm run format pnpm run validate ``` -------------------------------- ### Install zenQuery Preact Integration Source: https://github.com/sylphxltd/zen-query/blob/main/packages/preact/README.md Instructions to install the `@sylphlab/zen-query-preact` package and its necessary dependencies using pnpm. ```bash pnpm add @sylphlab/zen-query-client @sylphlab/zen-query-preact nanostores @nanostores/preact # You also need a transport package, e.g.: pnpm add @sylphlab/zenquery-transport-websocket ``` -------------------------------- ### Run zenQuery Backend Server Source: https://github.com/sylphxltd/zen-query/blob/main/examples/web-app/README.md This command starts the zenQuery backend server using `tsx` to execute the TypeScript file directly. The server typically listens on `ws://localhost:3000`. ```bash pnpm exec tsx server/index.ts ``` -------------------------------- ### Build zenQuery Web App for Production Source: https://github.com/sylphxltd/zen-query/blob/main/examples/web-app/README.md This command first compiles the server code and then builds the frontend application using Vite for production. The output will be in the `dist/` directory, ready for preview or deployment. ```bash pnpm run build ``` -------------------------------- ### Install zenQuery Server and Dependencies Source: https://github.com/sylphxltd/zen-query/blob/main/packages/server/README.md Installs the core zenQuery server package along with `zod` and `fast-json-patch`. A transport handler like `@sylphlab/zenquery-transport-websocket` is also required for full functionality. ```bash pnpm add @sylphlab/zenquery-server zod fast-json-patch # You also need a transport handler, e.g.: # pnpm add @sylphlab/zenquery-transport-websocket ``` -------------------------------- ### Install zenQuery React Integration and Dependencies Source: https://github.com/sylphxltd/zen-query/blob/main/packages/react/README.md Instructions for installing the `@sylphlab/zen-query-react` package along with its core dependencies like `@sylphlab/zen-query-client`, `nanostores`, `@nanostores/react`, and a transport package such as `@sylphlab/zenquery-transport-websocket` using pnpm. ```bash pnpm add @sylphlab/zen-query-client @sylphlab/zen-query-react nanostores @nanostores/react # You also need a transport package, e.g.: pnpm add @sylphlab/zenquery-transport-websocket ``` -------------------------------- ### ZenQuery Client and Nanostores Store Setup Source: https://github.com/sylphxltd/zen-query/blob/main/packages/client/README.md This snippet demonstrates how to initialize the ZenQuery client and define various Nanostores atoms for data fetching operations like queries, mutations (with optimistic updates), subscriptions, and hybrid patterns. It includes setting up the HTTP transport and binding helpers. ```typescript // store.ts import { atom } from 'nanostores'; import { createClient } from '@sylphlab/zen-query-client'; import { createHttpTransport } from '@sylphlab/zen-query-transport-http'; import type { AppRouter } from '../server/router'; // Your server router type export const $client = atom(() => // Type is inferred createClient({ transport: createHttpTransport({ url: '/api/zenquery', batching: true }), // Add other client options if needed }) ); // --- Binding Helpers --- import { query, subscription, hybrid, mutation, effect } from '@sylphlab/zen-query-client/nanostores'; // Query Atom export const $posts = query( // Selector function: receives 'get', returns the procedure reference get => get($client).posts.list, // Options object: input, initialData etc. (NO 'path' needed) { initialData: [] } // Assuming no input needed for list ); // Mutation Atom with Optimistic Update export const $addPost = mutation( // Selector function: returns the procedure reference get => get($client).posts.add, { // Options object (NO 'path' needed) effects: [ // Define optimistic updates effect($posts, (currentPosts, input) => { // Target atom, apply patch recipe const tempId = `temp-${Date.now()}`; const current = currentPosts ?? []; return [...current, { ...input, id: tempId, status: 'pending' }]; }), // Add effects for other atoms if needed ], // onSuccess: (result, input) => { console.log('Added:', result); }, // onError: (error, input) => { console.error('Failed:', error); }, }); // Subscription Atom (Example) export const $postsSub = subscription( get => get($client).posts.onUpdate, { /* input if needed */ } ); // Hybrid Atom (Combines Query and Subscription) export const $postsHybrid = hybrid($posts, $postsSub); ``` -------------------------------- ### Install zenQuery WebSocket Transport Source: https://github.com/sylphxltd/zen-query/blob/main/packages/transport-websocket/README.md Instructions on how to add the @sylphlab/zenquery-transport-websocket package to your project using pnpm. ```bash pnpm add @sylphlab/zenquery-transport-websocket ``` -------------------------------- ### Setup zenQuery Client Instance with Nanostores Atom Source: https://github.com/sylphxltd/zen-query/blob/main/packages/react/README.md Demonstrates how to create a Nanostore atom (`$client`) to hold the zenQuery client instance. It shows importing `atom` from `nanostores`, `createClient` from `@sylphlab/zen-query-client`, and `createWebSocketTransport` for communication, along with type inference for the server router. ```typescript // src/clientSetup.ts (Example) import { atom } from 'nanostores'; import { createClient } from '@sylphlab/zen-query-client'; import { createWebSocketTransport } from '@sylphlab/zenquery-transport-websocket'; import type { AppRouter } from '../server/router'; // Your server router type export const $client = atom(() => // Type is inferred createClient({ transport: createWebSocketTransport({ url: 'ws://...' }) }) ); ``` -------------------------------- ### Compile VSCode Extension and Webview Code Source: https://github.com/sylphxltd/zen-query/blob/main/examples/vscode-extension/README.md Compiles both the TypeScript code for the VSCode extension (output to `dist/`) and the React application for the webview (output to `dist/webview/`). This command uses `tsc` and `vite build` and must be run from the `examples/vscode-extension` directory. ```bash pnpm run compile ``` -------------------------------- ### Define zenQuery Router with Query, Mutation, and Subscription Procedures (TypeScript) Source: https://github.com/sylphxltd/zen-query/blob/main/packages/server/README.md This comprehensive example demonstrates how to initialize `zenQuery`, define a router, and implement `query`, `mutation`, and `subscription` procedures. It includes input validation with Zod, context handling, and real-time updates using `streamDiff` for subscriptions with `fast-json-patch`. ```typescript // src/router.ts (Example) import { initZenQuery, createRouter } from '@sylphlab/zenquery-server'; // Corrected import import { diffStatesToJsonPatch } from '@sylphlab/zenquery-server/utils'; // Diff helper import { z } from 'zod'; import { Operation as JsonPatchOperation, applyPatch } from 'fast-json-patch'; // Use fast-json-patch // Example Context (passed during request handling) interface Context { userId?: string; // Add other context properties like database connections, etc. } // Example Data Store & Events interface Todo { id: string; text: string; completed: boolean; } let todos: Record = {}; const todoEvents = new EventTarget(); // --- Initialize zenQuery with Context --- const t = initZenQuery(); // --- Define Procedures using Builder --- const getTodosProcedure = t.query({ // Use t.query resolve: async ({ ctx }) => { // Context type inferred console.log('Context in getTodos:', ctx); return Object.values(todos); }, }); const addTodoProcedure = t.mutation({ // Use t.mutation input: z.object({ text: z.string().min(1) }), // Input validation with Zod resolve: async ({ input, ctx }) => { // Input and Context types inferred if (!ctx.userId) throw new Error("Not authenticated"); // Example context usage const id = Math.random().toString(36).substring(7); const newTodo: Todo = { id, text: input.text, completed: false }; todos[id] = newTodo; // Dispatch event for subscription (or use a more robust mechanism) const patch: JsonPatchOperation[] = [{ op: 'add', path: `/${id}`, value: newTodo }]; todoEvents.dispatchEvent(new CustomEvent('update', { detail: patch })); return newTodo; }, }); // Example using .streamDiff for automatic patching const onTodoUpdateProcedure = t.subscription({ // Use t.subscription // Define the type of the full state yielded by the generator // Note: .streamDiff might implicitly set the final output to JsonPatch[] // but we might still need to define the yielded state type for the generator function // Let's assume we define the yielded state schema separately for clarity // const TodoMapSchema = z.record(z.object({ id: z.string(), text: z.string(), completed: z.boolean() })); // Use .streamDiff with an async generator yielding full state streamDiff: async function* ({ ctx }) { console.log('Subscription started for context:', ctx); let currentState = { ...todos }; // Get initial state yield currentState; // Yield initial state (diff helper handles this) // Create an async iterator from the event target const eventIterator = createEventIterator(todoEvents, 'update'); try { for await (const patch of eventIterator) { // Apply patch to get new state (server needs to track state for diffing) try { // Use fast-json-patch applyPatch currentState = applyPatch(currentState, patch).newDocument; yield currentState; // Yield the new full state } catch (e) { console.error("Failed to apply patch for diff stream:", e); // Decide how to handle patch errors (e.g., yield error, close stream) } } } finally { console.log('Subscription stopped for context:', ctx); // Cleanup logic for the iterator if needed // eventIterator.return?.(); } } }); // Helper to convert EventTarget to AsyncIterator (conceptual) async function* createEventIterator(target: EventTarget, eventName: string): AsyncGenerator { let listener: ((event: Event) => void) | null = null; const queue: T[] = []; let resolvePromise: (() => void) | null = null; listener = (event: Event) => { queue.push((event as CustomEvent).detail as T); resolvePromise?.(); resolvePromise = null; }; target.addEventListener(eventName, listener); try { while (true) { if (queue.length > 0) { yield queue.shift()!; } else { await new Promise((r) => resolvePromise = r); } } } finally { if (listener) target.removeEventListener(eventName, listener); } } // --- Create Router --- export const appRouter = createRouter()({ // Pass Context type // Group procedures under namespaces (e.g., 'todos') todos: { list: getTodosProcedure, add: addTodoProcedure, ``` -------------------------------- ### Define zenQuery Atoms for Data Operations Source: https://github.com/sylphxltd/zen-query/blob/main/packages/preact/README.md Demonstrates how to create Nanostore atoms for queries, mutations, subscriptions, and hybrid operations using `@sylphlab/zen-query-client/nanostores` helpers. Includes an example of optimistic updates for mutations. ```typescript // src/store.ts (Example) import { $client } from './clientSetup'; import { query, subscription, hybrid, mutation, effect } from '@sylphlab/zen-query-client/nanostores'; import { atom } from 'nanostores'; // Import base atom if needed // Query Atom export const $posts = query( // Selector function: receives 'get', returns the procedure reference get => get($client).posts.list, // Options object: input, initialData etc. (NO 'path' needed) { initialData: [] } // Assuming no input needed for list ); // Mutation Atom export const $addPost = mutation( // Selector function: returns the procedure reference get => get($client).posts.add, { // Options object (NO 'path' needed) effects: [ // Define optimistic updates effect($posts, (currentPosts, input) => { // Target atom, apply patch recipe const tempId = `temp-${Date.now()}`; const current = currentPosts ?? []; return [...current, { ...input, id: tempId, status: 'pending' }]; }) ] }); // Subscription Atom (Example) export const $postsSub = subscription( get => get($client).posts.onUpdate, { /* input if needed */ } ); // Hybrid Atom (Combines Query and Subscription) export const $postsHybrid = hybrid($posts, $postsSub); ``` -------------------------------- ### ZenQuery Client Usage with Nanostores for State Management Source: https://github.com/sylphxltd/zen-query/blob/main/README.md This TypeScript code demonstrates how to use `@sylphlab/zenquery-client` with `@nanostores/react` to interact with the ZenQuery server. It sets up a client atom, a query atom (`$todos`) to fetch and manage todo list state, and a mutation atom (`$addTodo`) for adding new todos, showcasing optimistic updates using the `effect` helper. ```typescript // Example Usage with Nanostores (@nanostores/react) // --- store.ts --- import { atom } from 'nanostores'; import { createClient } from '@sylphlab/zenquery-client'; import { query, mutation, effect } from '@sylphlab/zenquery-client/nanostores'; import { createHttpTransport } from '@sylphlab/zenquery-transport-http'; import type { AppRouter } from '../server/router'; // Your server router type // Assume Todo type is defined interface Todo { id: string; text: string; completed: boolean; status?: string } // 1. Client Atom export const $client = atom(() => // Type is inferred createClient({ transport: createHttpTransport({ url: '/api/zenquery', batching: true }) }) ); // 2. Query Atom export const $todos = query( // Selector function: receives the client, returns the procedure reference object client => client.todos.list, // Updated selector to match procedureSelector type // Options object: input and initialData (NO 'path' needed) { input: { limit: 10 }, initialData: [] } ); // 3. Mutation Atom export const $addTodo = mutation( // Selector function: returns the procedure reference get => get($client).todos.add, // Selector just returns the procedure { // Options object (NO 'path' needed) effects: [ // Define optimistic updates effect($todos, (currentTodos, input) => { // Target atom, apply patch recipe const tempId = `temp-${Date.now()}`; // Return the new state for the target atom return [...(currentTodos ?? []), { ...input, id: tempId, completed: false, status: 'pending' }]; }) ] // onSuccess: (result, input) => { ... }, // onError: (error, input) => { ... }, } ); ``` -------------------------------- ### Integrating zenQuery Router with WebSocket Transport Source: https://github.com/sylphxltd/zen-query/blob/main/packages/server/README.md This TypeScript example shows how to expose the `appRouter` via a WebSocket transport using `@sylphlab/zenquery-transport-websocket/server`. It sets up a `ws.Server`, creates a `createWebSocketHandler`, and attaches it to incoming WebSocket connections, demonstrating context creation for requests by extracting a user ID from headers. ```typescript // src/server.ts (Example using WebSocket transport) import { createWebSocketHandler } from '@sylphlab/zenquery-transport-websocket/server'; // Example import { appRouter } from './router'; import ws from 'ws'; // Example WebSocket server library const wss = new ws.Server({ port: 3000 }); const handler = createWebSocketHandler({ router: appRouter, // Optional: Define how to create context for each connection/request createContext: async ({ req }) => { // Example: Extract user ID from headers or session const userId = req.headers['x-user-id'] as string | undefined; return { userId }; }, }); wss.on('connection', (ws, req) => { console.log('Client connected'); handler({ ws, req }); // Attach zenQuery handler to the WebSocket connection ws.on('close', () => console.log('Client disconnected')); }); console.log('WebSocket server started on ws://localhost:3000'); ``` -------------------------------- ### Define ZenQuery Server Router Type in TypeScript Source: https://github.com/sylphxltd/zen-query/blob/main/packages/client/README.md Example of defining and exporting the type of the server router (`AppRouter`) using `@sylphlab/zenquery-server`. This type is crucial for end-to-end typesafety on the client. ```typescript // server.ts (Example) import { initZenQuery, createRouter } from '@sylphlab/zenquery-server'; // ... define procedures using t.query, t.mutation, t.subscription ... const t = initZenQuery(); const appRouter = createRouter()({ /* ... procedures ... */ }); export type AppRouter = typeof appRouter; // Export the type ``` -------------------------------- ### Using ZenQuery Hybrid Atoms in React Components Source: https://github.com/sylphxltd/zen-query/blob/main/README.md This snippet demonstrates how to combine a query atom and a subscription atom using the 'hybrid' helper, and then integrate the resulting hybrid atom into a React component using '@nanostores/react's 'useStore' hook to manage and display todo items, including optimistic updates. ```typescript // 4. (Optional) Hybrid Atom using the 'hybrid' helper // Combines a query atom and a subscription atom const $todosSub = subscription( get => get($client).todos.onUpdate, { /* input if needed */ } ); const $todosHybrid = hybrid($todos, $todosSub); // Combines query and subscription // --- Component.tsx --- import React from 'react'; import { useStore } from '@nanostores/react'; import { $todos, $addTodo } from './store'; function TodoManager() { // 4. Use atoms in component // Can use the query atom directly, or the hybrid atom const { data: todos, loading, error, status } = useStore($todosHybrid); // Using hybrid atom const { mutate: addTodo, loading: isAdding } = useStore($addTodo); const handleAdd = () => { addTodo({ text: 'New todo via zenQuery!' }); }; if (status === 'loading' && !todos?.length) return
Loading...
; if (status === 'error') return
Error: {error?.message}
; return (
    {todos?.map(todo => (
  • {todo.text} {todo.status === 'pending' ? '(Sending...)' : ''}
  • ))}
{/* Hybrid atom automatically reflects subscription updates */}
); } ``` -------------------------------- ### Define ZenQuery Server Router with Queries, Mutations, and Subscriptions Source: https://github.com/sylphxltd/zen-query/blob/main/README.md This TypeScript code defines an `appRouter` using `@sylphlab/zenquery-server`. It includes a `getTodos` query to fetch all todos, an `addTodo` mutation to add new todos (with an in-memory store and event dispatching), and an `onTodoUpdate` subscription that streams JSON Patch operations for real-time updates. It utilizes `zod` for input validation and `EventTarget` for internal event handling. ```typescript // packages/server/src/router.ts (Example Definition) import { initZenQuery, createRouter } from '@sylphlab/zenquery-server'; // Updated import import { z } from 'zod'; import { observable } from '@trpc/server/observable'; // Or your preferred observable library import { applyPatch, Operation as JsonPatchOperation } from 'rfc6902'; // JSON Patch interface Todo { id: string; text: string; completed: boolean; } // Simple in-memory store and event emitter for the example let todos: Record = {}; const todoEvents = new EventTarget(); // Use a simple EventTarget for demo // Initialize zenQuery (assuming a context type, adjust as needed) const t = initZenQuery(); const appRouter = createRouter() .procedure('getTodos', t.query({ // Use procedure helper resolve: async () => { return Object.values(todos); }, })) .procedure('addTodo', t.mutation({ // Use procedure helper input: z.object({ text: z.string() }), resolve: async ({ input }) => { const id = Math.random().toString(36).substring(7); const newTodo: Todo = { id, text: input.text, completed: false }; todos[id] = newTodo; // Dispatch event for subscription const detail: JsonPatchOperation[] = [{ op: 'add', path: `/${id}`, value: newTodo }]; todoEvents.dispatchEvent(new CustomEvent('update', { detail })); return newTodo; }, })) .procedure('onTodoUpdate', t.subscription({ // Use procedure helper // Output schema for the stream subscriptionOutput: z.array(z.any()), // Define JSON Patch schema properly later // Async generator for the stream stream: async function* () { // This needs proper implementation using async iterators / event listeners // The observable example below is from tRPC and needs adaptation // Conceptual: // const listener = createUpdateListener(); // try { // for await (const patch of listener) { // yield patch; // } // } finally { // listener.cleanup(); // } // Placeholder based on old example structure (needs rewrite) yield* observable((emit) => { const handler = (event: Event) => { emit.next((event as CustomEvent).detail); }; // 1. Emit initial state (optional, could also be a separate query) // emit.next([{ op: 'replace', path: '', value: todos }]); // Example initial state emission // 2. Emit deltas on change todoEvents.addEventListener('update', handler); return () => { todoEvents.removeEventListener('update', handler); }; }) as any; // Cast needed as observable isn't AsyncGenerator } })); export type AppRouter = typeof appRouter; // --- In your server setup --- // import { createHTTPHandler } from '@sylphlab/zenquery-transport-http/server'; // Example // import { createWebSocketHandler } from '@sylphlab/zenquery-transport-websocket/server'; // Example // ... create transport handler with appRouter ... ``` -------------------------------- ### Set Up zenQuery Client Nanostore Atom Source: https://github.com/sylphxltd/zen-query/blob/main/packages/preact/README.md Create a Nanostore atom to hold the zenQuery client instance, configured with a WebSocket transport and typed with your server router. ```typescript // src/clientSetup.ts (Example) import { atom } from 'nanostores'; import { createClient } from '@sylphlab/zen-query-client'; import { createWebSocketTransport } from '@sylphlab/zenquery-transport-websocket'; import type { AppRouter } from '../server/router'; // Your server router type export const $client = atom(() => // Type is inferred createClient({ transport: createWebSocketTransport({ url: 'ws://...' }) }) ); ``` -------------------------------- ### Create ZenQuery Client Instance with Nanostores Source: https://github.com/sylphxltd/zen-query/blob/main/packages/client/README.md Demonstrates how to create a typesafe zenQuery client using `createClient` from `@sylphlab/zen-query-client` and a chosen transport (e.g., WebSocket). The client instance is wrapped in a Nanostore atom for state management. ```typescript // clientSetup.ts (Example) import { atom } from 'nanostores'; import { createClient } from '@sylphlab/zen-query-client'; import { createWebSocketTransport } from '@sylphlab/zen-query-transport-websocket'; // Choose your transport import type { AppRouter } from './server'; // Import the router TYPE // Configure and create the transport const transport = createWebSocketTransport({ url: 'ws://localhost:3000' }); // Create the client instance (likely within a Nanostore atom) export const $client = atom(() => // Type is inferred createClient({ transport }) ); // Now use the binding helpers with the client atom! // See usage examples with Nanostores binding helpers below ``` -------------------------------- ### zenQuery Core Features and API Reference Source: https://github.com/sylphxltd/zen-query/blob/main/packages/server/README.md This section details the core features and API methods provided by zenQuery, including initialization, router creation, and defining different procedure types (query, mutation, subscription) with advanced capabilities like streaming, diffing, input validation, and context management. ```APIDOC initZenQuery: Initializes the builder instance with a context type. createRouter: Combines procedures into a typed router. t.query: Defines data fetching procedures using the QueryBuilder. t.mutation: Defines data modification procedures using the MutationBuilder. t.subscription: Defines realtime procedures using the SubscriptionBuilder. .stream(): Uses an Async Generator (yield) to push raw data. .streamDiff(): Uses an Async Generator (yield) to push full state, automatically diffing and sending JSON Patches. .resolveInitial(): Optional method to send an initial state snapshot. Input Validation: Use Zod schemas with the .input() method for automatic parsing and validation. Context: Pass request-specific context (like authentication data, database connections) to your procedures via createContext in the transport handler. Typesafety: Automatically infers types for client-side usage. ``` -------------------------------- ### Client-side Usage of zenQuery WebSocket Transport Source: https://github.com/sylphxltd/zen-query/blob/main/packages/transport-websocket/README.md Demonstrates how to set up a zenQuery client using the WebSocket transport. It shows importing necessary modules, creating a WebSocket transport instance, and initializing the client with a specified server URL. ```typescript // Client-side Usage import { createClient } from '@sylphlab/zenquery-client'; import { createWebSocketTransport } from '@sylphlab/zenquery-transport-websocket'; import type { AppRouter } from './server'; // Your server router type const transport = createWebSocketTransport({ url: 'ws://localhost:3000' }); const client = createClient({ transport }); ``` -------------------------------- ### Development Commands for zenQuery WebSocket Transport Source: https://github.com/sylphxltd/zen-query/blob/main/packages/transport-websocket/README.md Common development commands for building, testing, linting, and formatting the zenQuery WebSocket transport project. ```bash pnpm build pnpm test pnpm lint pnpm format ``` -------------------------------- ### React Component Using ZenQuery Nanostores Atoms Source: https://github.com/sylphxltd/zen-query/blob/main/packages/client/README.md This React component demonstrates how to consume the Nanostores atoms defined with ZenQuery. It uses `useStore` from `@nanostores/react` to access data, loading states, and mutation functions, showcasing a post manager with optimistic updates. ```typescript // --- Component Usage (React Example) --- import React from 'react'; import { useStore } from '@nanostores/react'; import { $posts, $addPost } from './store'; function PostManager() { // Use the hybrid atom to get initial data + realtime updates const { data: posts, loading, error, status } = useStore($postsHybrid); const { mutate: addPost, loading: isAdding } = useStore($addPost); const handleAdd = () => { addPost({ title: 'New Post via Nanostores', content: '...' }); }; // Use status for more granular loading/error states if (status === 'loading' && !posts?.length) return
Loading posts...
; if (status === 'error') return
Error loading posts: {error?.message}
; return (
    {posts?.map(post => (
  • {post.title} {post.status === 'pending' ? '(Sending...)' : ''}
  • ))}
); } ``` -------------------------------- ### Define zenQuery Atoms for Data Operations (Query, Mutation, Subscription, Hybrid) Source: https://github.com/sylphxltd/zen-query/blob/main/packages/react/README.md Illustrates how to create various Nanostore atoms for zenQuery operations: `query` for fetching data, `mutation` for modifying data (including optimistic updates using `effect`), `subscription` for real-time updates, and `hybrid` for combining query and subscription. It uses helpers from `@sylphlab/zen-query-client/nanostores`. ```typescript // src/store.ts (Example) import { $client } from './clientSetup'; import { query, subscription, hybrid, mutation, effect } from '@sylphlab/zen-query-client/nanostores'; import { atom } from 'nanostores'; // Import base atom if needed // Query Atom export const $posts = query( // Selector function: receives 'get', returns the procedure reference get => get($client).posts.list, // Options object: input, initialData etc. (NO 'path' needed) { initialData: [] } // Assuming no input needed for list ); // Mutation Atom export const $addPost = mutation( // Selector function: returns the procedure reference get => get($client).posts.add, { // Options object (NO 'path' needed) effects: [ // Define optimistic updates effect($posts, (currentPosts, input) => { // Target atom, apply patch recipe const tempId = `temp-${Date.now()}`; const current = currentPosts ?? []; return [...current, { ...input, id: tempId, status: 'pending' }]; }) ] }); // Subscription Atom (Example) export const $postsSub = subscription( get => get($client).posts.onUpdate, { /* input if needed */ } ); // Hybrid Atom (Combines Query and Subscription) export const $postsHybrid = hybrid($posts, $postsSub); ``` -------------------------------- ### Integrate zenQuery Atoms into Preact Components Source: https://github.com/sylphxltd/zen-query/blob/main/packages/preact/README.md Illustrates how to use the `useStore` hook from `@nanostores/preact` to consume zenQuery atoms within a Preact component, handling loading, error, and data states for queries and mutations. ```typescript // src/components/PostManager.tsx (Example) import { h } from 'preact'; import { useStore } from '@nanostores/preact'; import { $posts, $addPost } from '../store'; function PostManager() { // Use the hybrid atom to get initial data + realtime updates const { data: posts, loading, error, status } = useStore($postsHybrid); const { mutate: addPost, loading: isAdding } = useStore($addPost); const handleAdd = () => { addPost({ title: 'New Post via Preact', content: '...' }); }; // Use status for more granular loading/error states if (status === 'loading' && !posts?.length) return
Loading posts...
; if (status === 'error') return
Error loading posts: {error?.message}
; return (
    {posts?.map(post => (
  • {post.title} {post.status === 'pending' ? '(Sending...)' : ''}
  • ))}
); } export default PostManager; ``` -------------------------------- ### Defining zenQuery App Router with Procedures Source: https://github.com/sylphxltd/zen-query/blob/main/packages/server/README.md This TypeScript snippet demonstrates how to define an `appRouter` using `initZenQuery` and `createRouter`. It shows the structure for including procedures like `onTodoUpdateProcedure` and exports the `AppRouter` type for client-side usage, indicating where other procedure groups can be added. ```typescript onUpdate: onTodoUpdateProcedure, }, // You can add other procedure groups here // health: query({ resolve: () => 'ok' }) }); // Export the type for the client export type AppRouter = typeof appRouter; ``` -------------------------------- ### Integrate zenQuery Atoms into React Components with useStore Source: https://github.com/sylphxltd/zen-query/blob/main/packages/react/README.md Shows how to consume the zenQuery Nanostore atoms within React components using the `useStore` hook from `@nanostores/react`. It demonstrates fetching data, handling loading and error states, performing mutations, and rendering a list with optimistic updates. ```typescript // src/components/PostManager.tsx (Example) import React from 'react'; import { useStore } from '@nanostores/react'; import { $posts, $addPost } from '../store'; function PostManager() { // Use the hybrid atom to get initial data + realtime updates const { data: posts, loading, error, status } = useStore($postsHybrid); const { mutate: addPost, loading: isAdding } = useStore($addPost); const handleAdd = () => { addPost({ title: 'New Post via React', content: '...' }); }; // Use status for more granular loading/error states if (status === 'loading' && !posts?.length) return
Loading posts...
; if (status === 'error') return
Error loading posts: {error?.message}
; return (
    {posts?.map(post => (
  • {post.title} {post.status === 'pending' ? '(Sending...)' : ''}
  • ))}
); } export default PostManager; ``` -------------------------------- ### Server-side Usage of zenQuery WebSocket Transport with ws Source: https://github.com/sylphxltd/zen-query/blob/main/packages/transport-websocket/README.md Illustrates how to integrate the zenQuery WebSocket handler with a Node.js WebSocket server (e.g., using the 'ws' library). It covers creating a WebSocket server, initializing the zenQuery handler with a router, and handling incoming connections. ```typescript // Server-side Usage (Example with ws) import { createWebSocketHandler } from '@sylphlab/zenquery-transport-websocket/server'; import { appRouter } from './router'; // Your app router instance import ws from 'ws'; const wss = new ws.Server({ port: 3000 }); const handler = createWebSocketHandler({ router: appRouter, createContext: async () => ({ /* ... */ }) }); wss.on('connection', (ws, req) => { handler({ ws, req }); }); console.log('WebSocket server started on ws://localhost:3000'); ``` -------------------------------- ### OptimisticSyncCoordinator Interface API Reference Source: https://github.com/sylphxltd/zen-query/blob/main/docs/design/optimistic-sync-coordinator.md This API documentation details the `OptimisticSyncCoordinator` interface, outlining its conceptual internal state, core methods for managing optimistic mutations and server synchronization, and event subscription methods for reacting to state changes and data updates. ```APIDOC OptimisticSyncCoordinator Interface: Description: Coordinates optimistic updates, handles server acknowledgements and rejections, and resolves conflicts between client-side optimistic state and server-side deltas. Internal State (Conceptual): confirmedServerSeq: number - Highest server sequence number confirmed and applied. pendingMutations: PendingMutation[] - Queue of mutations sent but not yet confirmed/rejected. Methods: generateClientSeq(): number Description: Generates a unique, monotonically increasing sequence number for a new client mutation. registerPendingMutation(data: { clientSeq: number; mutationInfo: any; optimisticPatches: Map; }): void Description: Registers a mutation that has been sent to the server but not yet acknowledged. Stores the mutation details and the optimistic patches applied to the client state. Parameters: data: object clientSeq: number - The client sequence number. mutationInfo: any - Information about the pending mutation. optimisticPatches: Map - Optimistic patches applied to the client state. confirmMutation(clientSeq: number): void Description: Processes a confirmation (Ack) message from the server for a specific mutation. Removes the confirmed mutation from the pending queue. May trigger onStateChange if the removal affects optimistic state calculation. Parameters: clientSeq: number - The sequence number of the confirmed mutation. rejectMutation(clientSeq: number, error?: any): void Description: Processes a rejection message from the server for a specific mutation. Calculates the necessary inverse patches to revert the optimistic update. Triggers the onRollback callback with the calculated inverse patches. Removes the rejected mutation from the pending queue. May trigger onStateChange. Parameters: clientSeq: number - The sequence number of the rejected mutation. error?: any - Optional error information from the server. processServerDelta(deltaMessage: ServerDeltaMessage, conflictResolver: ConflictResolutionStrategy): void Description: Processes an incoming delta message from the server. Checks sequence numbers (serverSeq, prevServerSeq) against confirmedServerSeq for ordering. Identifies relevant pending mutations. Calls the configured ConflictResolutionStrategy to resolve conflicts between the server delta and pending patches. Updates confirmedServerSeq. Triggers the onApplyDelta callback with the *resolved* delta. May trigger onStateChange if server data affects optimistic calculations. Parameters: deltaMessage: ServerDeltaMessage - The delta message received from the server. conflictResolver: ConflictResolutionStrategy - The strategy instance for resolving conflicts. getPendingPatches(): Map Description: Retrieves the aggregated optimistic patches from all currently valid pending mutations. This is used by external logic (e.g., Binding Helpers) to compute the optimistic state. onStateChange(callback: StateChangeCallback): () => void Description: Registers a callback to be invoked when the Coordinator's internal state changes in a way that might require recalculating the optimistic state (e.g., pending queue changes, confirmed sequence updates affecting conflict resolution). Parameters: callback: StateChangeCallback - The function to call on state change. Returns: A function to unsubscribe the callback. onApplyDelta(callback: ApplyDeltaCallback): () => void Description: Registers a callback to be invoked after processServerDelta has resolved conflicts and determined the final delta that should be applied to the confirmed state. Parameters: callback: ApplyDeltaCallback - The function to call with the resolved delta. Returns: A function to unsubscribe the callback. onRollback(callback: RollbackCallback): () => void Description: Registers a callback to be invoked when a mutation rejection requires rolling back optimistic updates. Parameters: callback: RollbackCallback - The function to call with the inverse patches needed for rollback. Returns: A function to unsubscribe the callback. ``` -------------------------------- ### TypeScript Type Definitions for OptimisticSyncCoordinator Source: https://github.com/sylphxltd/zen-query/blob/main/docs/design/optimistic-sync-coordinator.md Defines the core data structures and function signatures used by the OptimisticSyncCoordinator. These types include definitions for JSON patches, unique atom identifiers, pending mutations, server delta messages, and various callback functions essential for state management and conflict resolution within the synchronization process. ```typescript // Assuming JSON Patch (RFC 6902) format type Patch = { op: string; path: string; value?: any; from?: string }; // Unique identifier for a state atom (e.g., Nanostore atom) type AtomKey = string | symbol; // Structure for pending mutations in the queue type PendingMutation = { clientSeq: number; mutationInfo: any; // Metadata about the mutation (e.g., name, input) optimisticPatches: Map; // Patches applied optimistically // Note: Inverse patches might be stored here or calculated on demand during rollback }; // Structure for server delta messages type ServerDeltaMessage = { data: any; // The actual delta payload (e.g., JSON Patch array or full snapshot) serverSeq: number; prevServerSeq?: number; // Optional: Previous sequence number for strict ordering checks }; // Function signature for applying a resolved delta to the confirmed state type ApplyDeltaCallback = (resolvedDelta: any) => void; // 'any' should be refined based on delta format (e.g., Patch[]) // Function signature for applying inverse patches during rollback type RollbackCallback = (inversePatches: Map) => void; // Function signature for notifying that optimistic state needs recalculation type StateChangeCallback = () => void; // Interface for a configurable conflict resolution strategy interface ConflictResolutionStrategy { /** * Resolves conflicts between server delta and pending mutations. * @param serverDelta The incoming delta from the server. * @param pendingPatches Patches from mutations that haven't been confirmed before this delta. * @returns The resolved delta to be applied to the confirmed state. */ resolve(serverDelta: any, pendingPatches: Map): any; // 'any' should be refined } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.