diff --git a/docs/src/pages/en/(pages)/framework/http.mdx b/docs/src/pages/en/(pages)/framework/http.mdx index aec20011..5fa69df8 100644 --- a/docs/src/pages/en/(pages)/framework/http.mdx +++ b/docs/src/pages/en/(pages)/framework/http.mdx @@ -365,3 +365,101 @@ export default function MyComponent() { return
Render lock
; } ``` + + +## Logger + + +With `logger` you can log messages using the framework's built-in logger. The `logger` object provides `info`, `warn`, `error`, and `debug` methods that integrate with the framework's logging system, providing consistent and formatted output. + +```jsx +import { logger } from "@lazarv/react-server"; + +export default function MyComponent() { + logger.info("Rendering MyComponent"); + + returnHello World
; +} +``` + +The `logger` automatically uses the framework's Vite-integrated logger in development mode for nicely formatted output, and falls back to `console` in production. It is context-aware — when called inside an `after()` callback, the log output is annotated with an `(after)` label so you can distinguish post-response logs from rendering logs. + +```jsx +import { after, logger } from "@lazarv/react-server"; + +export default function MyComponent() { + logger.info("Rendering component"); + + after(() => { + logger.info("Response sent"); // logged with (after) label in dev + }); + + returnHello World
; +} +``` + +The available methods are: + +| Method | Description | +|---|---| +| `logger.info(msg, ...args)` | Log an informational message | +| `logger.warn(msg, ...args)` | Log a warning message | +| `logger.error(msg, ...args)` | Log an error message or `Error` object | +| `logger.debug(msg, ...args)` | Log a debug message | + +> **Note:** The `logger` can be used anywhere on the server — in components, server functions, middleware, route handlers, workers, and `after()` callbacks. It does not require a request context, but when one is available, it uses the context-specific logger instance. + + +## After + + +With `after()` you can register a callback function that runs **after the response has been sent** to the client. This is useful for performing cleanup tasks, logging, analytics, or any side effects that should not delay the response. + +```jsx +import { after, logger } from "@lazarv/react-server"; + +export default function MyComponent() { + after(() => { + logger.info("Response sent to client."); + }); + + returnHello World
; +} +``` + +The `after()` hook can be called multiple times to register multiple callbacks. All registered callbacks run concurrently via `Promise.allSettled` after the response stream completes, so one failing callback does not prevent the others from running. + +```jsx +import { after } from "@lazarv/react-server"; + +export default function MyComponent() { + after(async () => { + await saveAnalytics({ page: "/home", timestamp: Date.now() }); + }); + + after(async () => { + await cleanupTempFiles(); + }); + + returnHome
; +} +``` + +You can also use `after()` in server functions, middleware, route handlers, or any server-side code that runs within a request context: + +```jsx +import { after } from "@lazarv/react-server"; + +export async function submitForm(formData) { + "use server"; + + const data = Object.fromEntries(formData.entries()); + await saveToDatabase(data); + + after(async () => { + await sendNotificationEmail(data.email); + }); +} +``` + +> **Note:** The `after()` hook can only be called during a request. Calling it outside of a request context (e.g., at module scope or in a standalone script) will throw an error. diff --git a/docs/src/pages/en/(pages)/framework/mcp.mdx b/docs/src/pages/en/(pages)/framework/mcp.mdx index d059c191..4efac655 100644 --- a/docs/src/pages/en/(pages)/framework/mcp.mdx +++ b/docs/src/pages/en/(pages)/framework/mcp.mdx @@ -1,7 +1,7 @@ --- title: MCP category: Framework -order: 14 +order: 15 --- import Link from "../../../../components/Link.jsx"; diff --git a/docs/src/pages/en/(pages)/framework/worker.mdx b/docs/src/pages/en/(pages)/framework/worker.mdx new file mode 100644 index 00000000..e313d58a --- /dev/null +++ b/docs/src/pages/en/(pages)/framework/worker.mdx @@ -0,0 +1,571 @@ +--- +title: Workers +category: Framework +order: 14 +--- + +import Link from "../../../../components/Link.jsx"; + +# Workers + +The `"use worker"` directive in the `@lazarv/react-server` framework lets you offload heavy computations or blocking tasks to separate threads. On the server, functions marked with `"use worker"` run in a **Node.js Worker Thread** (`node:worker_threads`). On the client, the same directive runs your code in a **Web Worker** (browser `Worker` API). In both cases, you import and call worker functions as if they were ordinary async functions — the framework handles thread creation, message passing, and serialization transparently. + +All data flowing between the main thread and the worker is serialized using the **React Server Components (RSC) Flight protocol**. This means worker functions can return not only plain values but also **React elements**, **Suspense boundaries**, **Promises** (for deferred rendering with the `use()` hook), and **ReadableStreams**. + + +## Why Use Workers? + + +- **Non-blocking rendering:** CPU-intensive work (prime sieves, sorting, matrix math, image processing) runs off the main thread so it doesn't block server request handling or the browser UI. +- **Concurrency:** Multiple worker calls can execute in parallel, improving throughput. +- **Unified API:** The same `"use worker"` directive works in both server and client code. You write one module and the framework picks the right threading primitive for the environment. +- **RSC-native serialization:** Because data is serialized via the Flight protocol, you can seamlessly pass and return React elements, Suspense boundaries, ReadableStreams, and deferred Promises between threads. + + +## How It Works + + +When the framework encounters a file with `"use worker"` at the top, it replaces all exports with thin proxy functions at build time. The original module code is moved into a virtual module that runs inside the worker thread. When you call an exported function, the proxy: + +1. Serializes the arguments using the RSC Flight protocol. +2. Posts a message to the worker thread (or Web Worker) with the function name and serialized arguments. +3. The worker deserializes the arguments, executes the function, and serializes the return value back. +4. The proxy deserializes the result and resolves the returned Promise. + +`ReadableStream` values are **transferred** (zero-copy) between threads via the `postMessage` transfer list, making streaming results efficient. + + +## Server Workers + + +On the server, `"use worker"` modules run in a **Node.js Worker Thread**. The worker is spawned lazily on the first call and reused for subsequent calls. If the worker crashes, it is automatically restarted. + +### Basic usage + +Create a file with the `"use worker"` directive at the top and export async functions: + +```jsx +"use worker"; + +export async function computeFactorial(n) { + if (n <= 1) return 1; + return n * computeFactorial(n - 1); +} +``` + +Import and call it from any server component: + +```jsx +import { computeFactorial } from "./computeFactorial"; + +export default async function FactorialPage({ number }) { + const result = await computeFactorial(42); + return{result.digits} digits, computed in {result.duration}ms
+ )} +{JSON.stringify(data, null, 2)};
+}
+
+export function AnalysisPanel() {
+ const [result, setResult] = useState(null);
+
+ const run = useCallback(async () => {
+ const res = await analyzeDataset();
+ setResult(res);
+ }, []);
+
+ return (
+ "use worker"
+ + Offload computation to{" "} + Worker Threads{" "} + on the server and{" "} + Web Workers{" "} + in the browser — powered by{" "} + + @lazarv/react-server + +
+
+ Functions marked with{" "}
+
+ "use worker"
+ {" "}
+ run in a Node.js worker thread. They can return plain values,
+ React elements with Suspense, and ReadableStreams.
+
{description}
+ )} + {children} +
+ The same{" "}
+
+ "use worker"
+ {" "}
+ directive works in the browser, offloading computation to a Web Worker.
+ The main thread stays responsive while heavy work runs in parallel.
+
+ Check if this code is running inside a Web Worker using the{" "}
+
+ isWorker()
+ {" "}
+ helper from{" "}
+
+ @lazarv/react-server/worker
+
+
+ Iterative BigInt Fibonacci computed off the main thread +
+ ++ Generate and sort a large Float64Array in a Web Worker +
+ +
+ Worker returns an object containing a Promise, consumed via
+ React's use() hook
+
+ Real-time computation results streamed from a Web Worker +
+ + + + {streamData &&