### Install and Run Aljabr Demo Source: https://github.com/jasuperior/aljabr/blob/main/README.md Provides instructions to clone the Aljabr repository, install dependencies, and run the development server to try out the demo application. ```sh git clone https://github.com/jasuperior/aljabr.git cd aljabr npm install npm run dev ``` -------------------------------- ### Full Example Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/match.md A comprehensive example demonstrating the usage of `union`, `match`, `when`, `pred`, `__`, `Union`, and `getTag` for defining and matching message variants. ```APIDOC ## Full example ```ts import { union, match, when, pred, __, Union, getTag } from "aljabr" const Msg = union({ Text: (body: string) => ({ body }), Image: (url: string, alt: string) => ({ url, alt }), Deleted: { at: 0 }, }) type Msg = Union function render(msg: Msg): string { return match(msg, { Text: [ when({ body: pred((b) => b.length > 280) }, () => ""), when(__, ({ body }) => body), ], Image: ({ url, alt }) => `${alt}`, Deleted: ({ at }) => ``, }) } ``` ``` -------------------------------- ### Full example with union, match, and when Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/match.md A comprehensive example demonstrating the usage of `union`, `match`, `when`, `pred`, `__`, `Union`, and `getTag` for handling different message types. ```typescript import { union, match, when, pred, __, Union, getTag } from "aljabr" const Msg = union({ Text: (body: string) => ({ body }), Image: (url: string, alt: string) => ({ url, alt }), Deleted: { at: 0 }, }) type Msg = Union function render(msg: Msg): string { return match(msg, { Text: [ when({ body: pred((b) => b.length > 280) }, () => ""), when(__, ({ body }) => body), ], Image: ({ url, alt }) => `${alt}`, Deleted: ({ at }) => ``, }) } ``` -------------------------------- ### Examples of Resource Lifecycle Management Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/scope.md Illustrative examples demonstrating manual lifecycle management and integration with TC39's explicit resource management proposal. ```APIDOC ## Examples ### Manual lifecycle ```ts const scope = Scope() scope.defer(async () => { await cache.flush() console.log("cache flushed") }) const db = await scope.acquire(DbResource) const client = await scope.acquire(HttpClientResource) await runMigrations(db, client) await scope.dispose() // → client closes, db disconnects, cache flushes — LIFO ``` ### TC39 explicit resource management ```ts async function handleRequest(req: Request): Promise { await using scope = Scope() // Assumes Scope implements Symbol.asyncDispose const db = await scope.acquire(DbResource) const session = await scope.acquire(SessionResource) const user = await db.findUser(session.userId) return Response.json(user) // scope.dispose() fires automatically — session closes, db disconnects } ``` ``` -------------------------------- ### Custom Adapter Example (In-Memory) Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/persist.md Illustrates creating a custom in-memory adapter for testing purposes. ```APIDOC ## Custom adapter (in-memory, for testing) ### Description Example demonstrating the creation of a custom in-memory `PersistAdapter` for testing. ### Request Example ```ts import { type PersistAdapter, persistedSignal } from "aljabr/prelude" function memoryAdapter(): PersistAdapter { const store = new Map() return { get: (key) => store.get(key) ?? null, set: (key, value) => store.set(key, value), remove: (key) => store.delete(key), } } const sig = persistedSignal("initial", { key: "test.key", adapter: memoryAdapter(), }) ``` ``` -------------------------------- ### Full Panel Setup with Scope Management Source: https://github.com/jasuperior/aljabr/blob/main/docs/guides/advanced/resource-lifetime.md Illustrates a complete panel setup using a main scope to manage state, external resources, reactive derivations, side effects like URL synchronization and WebSocket updates, and event listeners. All teardown logic is deferred to the scope for LIFO execution. ```typescript async function mountOrderPanel(): Promise<() => Promise> { const panelScope = Scope() // State const view = Ref.create({ filter: "", sortField: "confirmedAt", sortDir: "desc", page: 1, selected: null, }) // External resource — client for the lifetime of the panel const client = await panelScope.acquire(DetailClientResource) // Reactive graph const visibleOrders = Derived.create(() => computeVisibleOrders(view, orders)) const summary = Derived.create(() => computeSummary(visibleOrders)) const orderDetail = AsyncDerived.create(async (signal) => { const id = view.get("selected") if (!id) return null as unknown as OrderDetail return client.get(`/${id}/detail`, { signal }) }) // Side effect — URL sync const urlSync = watchEffect(async () => { const params = buildParams(view) history.replaceState(null, "", `?${params}`) }) // WebSocket for live order updates const wsHandle = watchEffect( async (signal, scope) => { const ws = await scope.acquire(WsResource("orders")) ws.send(JSON.stringify({ type: "subscribe" })) return receiveNextMessage(ws, signal) }, (result) => match(result, { Done: ({ value }) => applyOrderUpdate(value, view), Failed: ({ error }) => console.error(error), Stale: () => {}, }), { eager: true }, ) // Keyboard shortcut const keyHandler = (e: KeyboardEvent) => { if (e.key === "Escape") view.set("selected", null) } document.addEventListener("keydown", keyHandler) // Register all teardown in the scope panelScope.defer(() => wsHandle.stop()) panelScope.defer(() => urlSync.stop()) panelScope.defer(() => document.removeEventListener("keydown", keyHandler)) panelScope.defer(() => view.dispose()) renderPanel({ view, visibleOrders, summary, orderDetail }) // Return the unmount function return () => panelScope.dispose() } // Usage const unmount = await mountOrderPanel() // Later — navigation, modal close, tab close await unmount() // wsHandle stopped → urlSync stopped → keyHandler removed → view disposed → client closed ``` -------------------------------- ### Install Aljabr Source: https://github.com/jasuperior/aljabr/blob/main/docs/guides/ui/canvas.md Install the Aljabr package using npm. ```sh npm install aljabr ``` -------------------------------- ### Install Aljabr Source: https://github.com/jasuperior/aljabr/blob/main/README.md Install the aljabr package using npm, pnpm, or yarn. ```sh npm install aljabr # pnpm add aljabr # yarn add aljabr ``` -------------------------------- ### Examples: Building and Evaluating Expression Trees Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/tree.md Illustrates building an expression tree and evaluating it using pattern matching. ```APIDOC ## Examples ### Building an expression tree ```ts type Expr = number | ["add", Expr, Expr] | ["mul", Expr, Expr] const exprTree = Tree.Branch( "add" as const, Tree.Branch("mul" as const, Tree.Leaf(2), Tree.Leaf(3)), Tree.Leaf(4), ) // Evaluate: (2 * 3) + 4 function evaluate(t: Tree): number { return match(t, { Leaf: ({ value }) => value as number, Branch: ({ value, left, right }) => { const l = evaluate(left as Tree) const r = evaluate(right as Tree) return value === "add" ? l + r : l * r }, }) } ``` ``` -------------------------------- ### Examples: Collecting Leaf Values Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/tree.md Shows how to collect all values from the leaves of a tree. ```APIDOC ### Collecting all leaf values ```ts // Using fold (less direct for leaves only) const leaves = tree.fold( (acc, v) => acc, // skip in fold — fold visits every node [], ) // For leaves only, use match recursively: function collectLeaves(t: Tree): T[] { return match(t, { Leaf: ({ value }) => [value], Branch: ({ left, right }) => [ ...collectLeaves(left), ...collectLeaves(right), ], }) } ``` ``` -------------------------------- ### Install aljabr Source: https://github.com/jasuperior/aljabr/blob/main/docs/guides/ui/dom.md Install the aljabr package using npm. Configure tsconfig.json for JSX if using it. ```sh npm install aljabr ``` ```json // tsconfig.json { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "aljabr/ui/dom" } } ``` -------------------------------- ### Example: Catch-all pattern Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/union.md Demonstrates the use of the catch-all pattern `__` in a `when()` arm, ensuring it always matches. ```typescript // Catch-all when(__, () => "fallback") ``` -------------------------------- ### Basic Effect Recovery Example Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/effect.md This example shows a simple recovery mechanism where a fallback fetch is attempted if the primary fetch fails. ```typescript const data = Effect.Idle(async (signal) => fetchFromPrimary(signal)) .recover(() => Effect.Idle(async (signal) => fetchFromFallback(signal))) const result = await data.run() ``` -------------------------------- ### Create and Mount Canvas Renderer Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/ui/canvas.md Example of creating a canvas renderer, mounting a component, and later unmounting it to clean up resources. ```ts import { createCanvasRenderer } from "aljabr/ui/canvas"; const canvas = document.querySelector("#scene")!; const r = createCanvasRenderer(canvas); const unmount = r.mount(() => ( )); // Later — tears down listeners, disposes reactive subscriptions, clears the canvas: unmount(); ``` -------------------------------- ### Custom Serialization Example Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/persist.md Demonstrates how to use custom serialization and deserialization functions for complex data types. ```APIDOC ## Custom serialization ### Description Example showing how to handle custom serialization and deserialization for a `DateRange` type. ### Request Example ```ts import { persistedSignal } from "aljabr/prelude" type DateRange = { from: Date; to: Date } const range = persistedSignal( { from: new Date(), to: new Date() }, { key: "filter.dateRange", serialize: ({ from, to }) => JSON.stringify({ from: from.toISOString(), to: to.toISOString() }), deserialize: (raw) => { const { from, to } = JSON.parse(raw) return { from: new Date(from), to: new Date(to) } }, }, ) ``` ``` -------------------------------- ### Event Bubbling Example Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/ui/canvas.md Demonstrates how events bubble up the scene graph and how `stopPropagation` can be used to prevent further propagation. ```APIDOC ### Bubbling Once a target is found, `bubbleEvent(target, native)` walks `parent` pointers from the target up to the root, dispatching each ancestor's matching `on*` prop (`pointerdown` → `onPointerDown`, `wheel` → `onWheel`, …) until either the chain terminates or a handler calls `event.stopPropagation()`. ```tsx console.log("group:", e.target.tag)}> { console.log("rect clicked"); e.stopPropagation(); // halts the bubble — group's onClick won't fire }} /> ``` ``` -------------------------------- ### Examples: Transforming and Measuring Tree Properties Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/tree.md Demonstrates using `map` to transform tree values and `depth` to measure tree height. ```APIDOC ### Transforming and measuring ```ts const tree = Tree.Branch( 10, Tree.Branch(5, Tree.Leaf(1), Tree.Leaf(3)), Tree.Leaf(20), ) const stringified = tree.map(n => `(${n})`) // stringified will be Branch("(10)", Branch("(5)", Leaf("(1)"), Leaf("(3)")), Leaf("(20)")) tree.depth() // 2 tree.fold((acc, n) => Math.max(acc, n), 0) // 20 ``` ``` -------------------------------- ### Example: Chaining Optional Lookups Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/option.md Shows how to chain multiple optional lookups and parsing operations using flatMap and getOrElse. ```ts const config: Map = loadConfig() const timeout = Option.Some(config) .flatMap(c => { const val = c.get("timeout") return val ? Option.Some(val) : Option.None() }) .flatMap(s => { const n = parseInt(s) return isNaN(n) ? Option.None() : Option.Some(n) }) .getOrElse(30_000) ``` -------------------------------- ### Context Module Test Setup Source: https://github.com/jasuperior/aljabr/blob/main/test-coverage-analysis.md Prepares tests for the Context module, focusing on getCurrentComputation, trackIn, createOwner, runInContext, untrack, scheduleNotification, and batch operations. ```typescript import { getCurrentComputation, trackIn, createOwner, runInContext, untrack, scheduleNotification, batch } from "../../src/prelude/context"; // null outside any context // push/pop semantics with trackIn // nested trackIn // createOwner parent-child linkage // dispose cascades to children and cleanups // untrack suppresses subscription // batch defers scheduleNotification ``` -------------------------------- ### Example: Array of `when()` arms in `match()` Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/union.md Demonstrates how to use an array of `when()` arms within a `match()` statement for sequential, first-match-wins pattern matching. ```typescript // Array of arms in match match(event, { KeyPress: [ when({ key: "Enter" }, () => "submit"), when({ key: is.union("Tab", "Escape") }, () => "navigation"), when({ key: select("k", is.not(is.nullish)) }, (_, { k }) => `char: ${k}`), when(__, () => "other"), ], }) ``` -------------------------------- ### Example: Guard-only pattern Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/union.md Shows how to use a guard function in `when()` to match a value based on a condition. ```typescript // Guard-only when((v) => v.x > 0, () => "positive x") ``` -------------------------------- ### Example: Combined pattern and guard Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/union.md Illustrates a `when()` arm that requires both a structural pattern match and a guard condition to be true. ```typescript // Pattern + guard when({ active: true }, (v) => v.count > 10, () => "active and busy") ``` -------------------------------- ### Example: Converting to Result for Error Propagation Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/option.md Demonstrates converting an Option to a Result to propagate errors, particularly for environment variable lookups. ```ts import { Result } from "aljabr/prelude" function requireEnv(key: string): Result { return Option.Some(process.env[key] ?? null) .flatMap(v => v ? Option.Some(v) : Option.None()) .toResult(`Missing required env var: ${key}`) } ``` -------------------------------- ### Example of Fault.Defect creation Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/fault.md Demonstrates how a TypeError can lead to a Fault.Defect when accessing properties of an undefined value. ```typescript // accessing .foo on null → TypeError → Fault.Defect({ thrown: TypeError }) async (signal) => { const data = await fetch(url, { signal }).then(r => r.json()) return data.foo.bar // TypeError if data.foo is undefined } ``` -------------------------------- ### Example: Deep structural match Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/union.md Shows how to match nested object properties within a structural pattern. ```typescript // Deep structural match when({ user: { name: "Alice" } }, () => "found Alice") ``` -------------------------------- ### Example: Safe Property Access with Option Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/option.md Illustrates using Option to safely access nested properties, providing a default value if any part of the chain is missing. ```ts function getCity(user: User | null): Option { if (!user) return Option.None() if (!user.address) return Option.None() return Option.Some(user.address.city) } const city = getCity(currentUser) .map(c => c.toUpperCase()) .getOrElse("UNKNOWN") ``` -------------------------------- ### Create a Basic Signal Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/signals.md Creates a reactive mutable value. The signal starts as 'Unset' if no initial value is provided, or 'Active' otherwise. Includes examples of getting and setting values, and functional updates. ```typescript const [count, setCount] = signal(0) count() // 0 setCount(1) count() // 1 // Functional update setCount(prev => (prev ?? 0) + 1) count() // 2 ``` -------------------------------- ### Component JSX Example Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/ui/dom.md Shows how to define and use a component using JSX syntax. The JSX component invocation is equivalent to the direct `view(ComponentName, props)` call. ```tsx function Greeting({ name }: { name: string }) { return

Hello, {name}

; } // JSX // Equivalent view(Greeting, { name: "world" }) ``` -------------------------------- ### Get Reactive Snapshot of Entire Array Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/derived-array.md Use get() when you need the full array as a value, for example, to pass to a non-reactive consumer or to serialize. This registers a coarse dependency on a root signal that is notified on every re-computation. ```typescript const evens = Ref.create([1, 2, 3, 4, 5]).filter(x => x % 2 === 0); evens.get(); // [2, 4] — tracked; re-runs when any element or length changes ``` -------------------------------- ### Use Viewport for Group Transforms Source: https://github.com/jasuperior/aljabr/blob/main/docs/roadmap/v0.3.8.md Example of using the Viewport factory to get pan/zoom signals and applying them as props to a root group element for rendering. ```tsx const vp = Viewport(canvasEl); {/* all diagram content */} ; ``` -------------------------------- ### Signal.create() Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/signal.md Demonstrates how to create new signals, with options for initial values and custom state protocols. ```APIDOC ## `Signal.create()` ```ts Signal.create(): Signal // starts Unset Signal.create(initial: T): Signal // starts Active Signal.create(initial: S, protocol: SignalProtocol): Signal // custom state ``` Creates a new signal. If created inside a reactive owner, the signal is automatically disposed when the owner is disposed. ```ts const count = Signal.create(0) // Signal — Active const deferred = Signal.create() // Signal — Unset // Custom state union const field = Signal.create( Validation.Unvalidated() as Validation, { extract: (state) => match(state, { Unvalidated: () => null, Valid: ({ value }) => value, Invalid: () => null, }), }, ) // Signal> ``` > **Type inference note:** TypeScript infers `S` from the `initial` argument. When the initial value is a narrow variant (e.g. `Unvalidated`), cast it to the full union type so `S` is inferred correctly: `Validation.Unvalidated() as Validation`. ``` -------------------------------- ### Mounting and Unmounting a View Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/ui/dom.md Demonstrates how to use `createRenderer` with `domHost` to mount a simple 'Hello world' heading and later unmount it. Ensure the target DOM element exists. ```typescript import { createRenderer, view } from "aljabr/ui"; import { domHost } from "aljabr/ui/dom"; const { mount } = createRenderer(domHost); const unmount = mount( () => view("h1", null, "Hello world"), document.getElementById("root")!, ); // Later — cleans up everything: unmount(); ``` -------------------------------- ### Initializing Renderer with canvasHost Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/ui/canvas.md Shows how to set up a renderer using the canvasHost for custom protocols. It requires a scheduleFlush function, typically using queueMicrotask. ```typescript import { createRenderer } from "aljabr/ui"; import { canvasHost } from "aljabr/ui/canvas"; const { mount } = createRenderer(canvasHost, { scheduleFlush(flush) { queueMicrotask(flush); }, }); ``` -------------------------------- ### Initialize Viewport and Renderer Source: https://github.com/jasuperior/aljabr/blob/main/docs/guides/ui/canvas.md Sets up the canvas, Viewport for pan/zoom, and a renderer. The root group applies viewport transforms, and off-screen content is culled by default. ```tsx import { Viewport, createCanvasRenderer } from "aljabr/ui/canvas"; const canvas = document.querySelector("#scene")!; const vp = Viewport(canvas); const r = createCanvasRenderer(canvas, { viewport: vp }); r.mount(() => ( {/* world-space content goes here */} )); // Pan and zoom by writing directly to the signals: vp.x.set(150); vp.scale.set(2); vp.reset(); // back to (0, 0, 1) ``` -------------------------------- ### Create Resource Instances Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/scope.md Instantiate `Resource` with specific acquisition and release logic for different types of resources like database connections, HTTP clients, or WebSockets. ```typescript // Database connection const DbResource = Resource( () => connectToDb(process.env.DB_URL), (db) => db.disconnect(), ) // HTTP client with async teardown const ClientResource = Resource( async () => { const client = new ApiClient() await client.authenticate() return client }, async (client) => client.close(), ) // WebSocket const WsResource = Resource( (url: string) => new Promise((resolve, reject) => { const ws = new WebSocket(url) ws.onopen = () => resolve(ws) ws.onerror = (e) => reject(e) }), (ws) => ws.close(), ) ``` -------------------------------- ### Get Item at Index with Reactive Dependency Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/derived-array.md Reads the item at index i and registers it as a reactive dependency in the active tracking context. Returns undefined for out-of-bounds indices and after disposal. Fine-grained: only the subscriber that called get(i) is notified when index i changes. ```typescript const evens = Ref.create([1, 2, 3, 4, 5]).filter(x => x % 2 === 0); evens.get(0); // 2 evens.get(1); // 4 evens.get(2); // undefined ``` -------------------------------- ### RefArray.at(i) Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/ref.md Getting a Derived handle for a specific index in the RefArray. ```APIDOC ### `.at(i)` Returns a `Derived` handle for index `i`. Each call creates a new `Derived`. Cache it if reused frequently. ```ts refArray.at(i: number): Derived ``` ```ts const firstHandle = items.at(0) // Derived firstHandle.get() // 1 ``` ``` -------------------------------- ### Create a Pannable and Zoomable Canvas Diagram Source: https://github.com/jasuperior/aljabr/blob/main/docs/guides/ui/canvas.md This example shows how to set up a canvas renderer, define a reactive node component, and implement wheel-to-zoom functionality. Use this for interactive diagrams where nodes can be selected and the view can be panned and zoomed. ```tsx /** @jsxImportSource aljabr/ui/canvas */ import { Signal } from "aljabr/prelude"; import { createCanvasRenderer, Viewport } from "aljabr/ui/canvas"; import type { CanvasSyntheticEvent } from "aljabr/ui/canvas"; const canvas = document.querySelector("#scene")!; const vp = Viewport(canvas); const r = createCanvasRenderer(canvas, { viewport: vp }); const selected = Signal.create(null); const Node = ({ id, x, y, label }: { id: string; x: number; y: number; label: string }) => ( (selected.get() === id ? "lightblue" : "white")} stroke="black" strokeWidth={1} textAlign="center" verticalAlign="middle" onClick={(e: CanvasSyntheticEvent) => { selected.set(id); e.stopPropagation(); }} > {label} ); r.mount(() => ( selected.set(null)} fontSize={14} fontFamily="sans-serif" > )); // Wheel-to-zoom — direct DOM listener, no synthetic event needed canvas.addEventListener("wheel", (e) => { e.preventDefault(); const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1; vp.scale.set((vp.scale.peek() ?? 1) * factor); }); ``` -------------------------------- ### See Also Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/tree.md Links to related documentation for `match` and `union`. ```APIDOC ## See also - [`match`](../match.md) — pattern match on `Leaf` and `Branch` - [`union`](../union.md) — how `Tree` is built with `.typed()` for recursive generic types ``` -------------------------------- ### RefArray.length() Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/ref.md Getting the current length of the array with reactive dependency tracking for size changes. ```APIDOC ### `.length()` Returns the current length of the array and registers it as a reactive dependency. Subscribers are notified **only when the array size changes**, not on element-only mutations. ```ts refArray.length(): number ``` ```ts const len = Derived.create(() => items.length()) items.push(4) // len invalidated (3 → 4) items.move(0, 3) // len NOT invalidated (size unchanged) ``` ``` -------------------------------- ### Initializing and Using Viewport with a Renderer Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/ui/canvas.md Demonstrates how to initialize the Viewport with an HTMLCanvasElement and integrate it with a createCanvasRenderer. Pan and zoom are controlled by updating the viewport's signals. ```tsx import { createCanvasRenderer, Viewport } from "aljabr/ui/canvas"; const canvas = document.querySelector("#scene")!; const vp = Viewport(canvas); const r = createCanvasRenderer(canvas, { viewport: vp }); r.mount(() => ( {/* world-space content */} )); // Pan / zoom by writing the signals directly: vp.x.set(100); vp.scale.set(2); vp.reset(); // back to (0, 0, 1) ``` -------------------------------- ### PersistAdapter Interface Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/persist.md Defines the interface for storage backends, including get, set, and remove operations. ```APIDOC ## PersistAdapter ### Description The interface any storage backend must implement. Both built-in adapters satisfy this contract; implement your own to target other stores (IndexedDB, a remote API, in-memory, etc.). ### Interface Definition ```ts type PersistAdapter = { get(key: string): string | null set(key: string, value: string): void remove(key: string): void } ``` ``` -------------------------------- ### Pattern Matching with Result Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/result.md Example of using the 'match' function to destructure and handle different Result variants. ```typescript import { match } from "aljabr" match(result, { Accept: ({ value }) => `success: ${value}`, Expect: ({ pending }) => `loading...`, Reject: ({ error }) => `error: ${error}`, }) ``` -------------------------------- ### Fault Module Test Setup Source: https://github.com/jasuperior/aljabr/blob/main/test-coverage-analysis.md Sets up tests for the Fault module, including variant construction, tag/field verification, exhaustive matching, instanceOf detection, and generic type inference. ```typescript import { Fault } from "../../src/prelude/fault"; import { getTag, instanceOf } from "../../src/union"; import { match } from "../../src/match"; import { expectTypeOf } from "vitest"; // Variant construction and tag/field verification // Exhaustive match() // instanceOf(Fault.Fail, e) detection // Generic type inference ``` -------------------------------- ### Example: Structural pattern with `is.number` wildcard Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/union.md Uses `is.number` to match a field that must be of the number type. ```typescript // Pattern with is.* wildcard when({ age: is.number }, ({ age }) => age * 2) ``` -------------------------------- ### Pattern Matching with Option Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/option.md Demonstrates how to use the 'match' function for pattern matching on Option variants. ```ts import { match } from "aljabr" match(option, { Some: ({ value }) => `got: ${value}`, None: () => "nothing here", }) ``` -------------------------------- ### Ref Core Functionality Source: https://github.com/jasuperior/aljabr/blob/main/todo/ref-mutable-state.md Core methods for creating, getting, setting, patching, and disposing of Ref instances. ```APIDOC ## Ref Class ### Description A reactive mutable container for objects and arrays. `Ref` decomposes a structured value into per-leaf `Signal` nodes internally, enabling fine-grained path-level subscriptions. ### Methods #### `static create(initial: T): Ref` Creates a new `Ref` instance with the given initial value. #### `get

>(path: P): PathValue` Tracked read — registers exactly `path` as a dependency in the current Computation. One call = one subscription. Does NOT subscribe to intermediate paths. #### `set

>(path: P, value: PathValue): void` Replace the subtree at `path` with `value`. Notifies ALL leaf Signal subscribers under `path` — no diffing. Equality guard: if ref-equal to current value, no notification is emitted. #### `patch

>(path: P, value: PathValue): void` Deep structural diff of old vs new value at `path`. Only notifies leaf Signals whose values actually changed. Equality guard applied at each node before recursing (Strategy C). #### `dispose(): void` Dispose all internal leaf Signals and remove from owner tree. ### Path Type Machinery - `type Path`: Dot-separated string paths into T, including array indices (e.g. "users.0.name"). Dot notation only — no bracket syntax. Array indices are dots. - `type PathValue>`: The value type at a given path. - Path depth limit is a TypeScript recursion concern, not a runtime one. Real-world paths are shallow. ``` -------------------------------- ### Using Optimistic Signals Source: https://github.com/jasuperior/aljabr/blob/main/docs/guides/advanced/signal-protocols.md Demonstrates how to use `createOptimisticSignal` for managing optimistic updates and handling different states like Confirmed, Optimistic, and Conflicted. Use `.get()` for values and `.state()` for state-dependent logic. ```typescript const displayName = createOptimisticSignal("Alice") // User edits their display name saveButton.addEventListener("click", () => { const newName = nameInput.value displayName.apply(newName, (v) => api.updateDisplayName(v)) }) // Downstream — always sees the local value (confirmed or optimistic) const greeting = Derived.create(() => `Hello, ${displayName.signal.get() ?? "..."}` ) // UI — shows conflict resolution UI when needed const nameFieldState = Derived.create(() => match(displayName.signal.state(), { Confirmed: ({ value }) => ({ type: "stable", value }), Optimistic: ({ value }) => ({ type: "saving", value }), Conflicted: ({ local, server }) => ({ type: "conflict", local, server, acceptLocal: () => displayName.accept(), acceptServer: () => displayName.revert(), }), }) ) ``` -------------------------------- ### Create Ref and RefArray Instances Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/ref.md Demonstrates creating Ref and RefArray instances with different initial values and types. Use Ref.create() with an object for a Ref, and with an array for a RefArray. An unset Ref can be created with no arguments. ```typescript Ref.create(initial: T): RefArray // array (explicit type arg) → RefArray Ref.create(initial: T[]): RefArray // array → RefArray Ref.create(initial: T): Ref // object → Ref (active state) Ref.create(): Ref // no value → Ref (Unset state) ``` ```typescript // Object Ref (active) const state = Ref.create({ user: { name: "Alice", age: 30 }, scores: [1, 2, 3], active: true, }) // Root array → RefArray const items = Ref.create([1, 2, 3, 4, 5]) items.push(6) // RefArray methods available at the root items.length() // 6 // Unset — no initial value; get() returns undefined until first set() const pending = Ref.create<{ name: string }>() pending.isUnset // true ``` -------------------------------- ### Queued Scheduler with Protocol Source: https://github.com/jasuperior/aljabr/blob/main/docs/guides/advanced/renderer-protocol.md This demonstrates how a scheduler can be implemented using a queue and a protocol's scheduleFlush function to batch updates. ```ts let pending = false; const queue = new Set<() => void>(); const schedule = (fn: () => void): void => { queue.add(fn); if (!pending) { pending = true; protocol.scheduleFlush(() => { pending = false; const toRun = [...queue]; queue.clear(); for (const f of toRun) f(); }); } }; ``` -------------------------------- ### Traversable Methods Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/tree.md Details the shared behaviors available on all Tree variants: map, fold, and depth, with usage examples. ```APIDOC ## Traversable — Shared Behavior ### `.map(fn)` Applies a function to every node value, preserving the tree structure. ```ts .map(fn: (value: T) => U): Tree ``` **Example:** ```ts const nums = Tree.Branch(1, Tree.Leaf(2), Tree.Leaf(3)) const doubled = nums.map(n => n * 2) // doubled will be Branch(2, Leaf(4), Leaf(6)) ``` ### `.fold(fn, initial)` Reduces the tree to a single value using pre-order traversal (root → left → right). ```ts .fold(fn: (acc: U, value: T) => U, initial: U): U ``` **Example:** ```ts const tree = Tree.Branch( 1, Tree.Branch(2, Tree.Leaf(4), Tree.Leaf(5)), Tree.Leaf(3), ) const sum = tree.fold((acc, v) => acc + v, 0) // sum will be 15 (1 + 2 + 4 + 5 + 3) const values = tree.fold((acc, v) => [...acc, v], [] as number[]) // values will be [1, 2, 4, 5, 3] (pre-order traversal) ``` ### `.depth()` Returns the height of the tree: `0` for a `Leaf`, `1 + max(left.depth(), right.depth())` for a `Branch`. ```ts .depth(): number ``` **Examples:** ```ts Tree.Leaf(1).depth() // 0 Tree.Branch( 1, Tree.Branch(2, Tree.Leaf(4), Tree.Leaf(5)), Tree.Leaf(3), ).depth() // 2 ``` ``` -------------------------------- ### Option Variants: Some and None Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/option.md Demonstrates the creation and basic usage of Option.Some and Option.None. ```ts const name = Option.Some("alice") name.value // "alice" ``` ```ts const missing = Option.None() missing.value // null ``` -------------------------------- ### Building an Expression Tree Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/tree.md Example of constructing a Tree to represent a mathematical expression and evaluating it using pattern matching. ```typescript type Expr = number | ["add", Expr, Expr] | ["mul", Expr, Expr] const exprTree = Tree.Branch( "add" as const, Tree.Branch("mul" as const, Tree.Leaf(2), Tree.Leaf(3)), Tree.Leaf(4), ) // Evaluate: (2 * 3) + 4 function evaluate(t: Tree): number { return match(t, { Leaf: ({ value }) => value as number, Branch: ({ value, left, right }) => { const l = evaluate(left as Tree) const r = evaluate(right as Tree) return value === "add" ? l + r : l * r }, }) } ``` -------------------------------- ### Decoding with LazySchema Source: https://github.com/jasuperior/aljabr/blob/main/docs/roadmap/v0.3.10.md The implementation detail for decoding when encountering a LazySchema. It evaluates the thunk to get the schema and then proceeds with decoding. ```typescript LazySchema: ({ thunk }) => _decode((thunk as () => AnySchema)(), input, path), ``` -------------------------------- ### Create DOM Renderer Source: https://github.com/jasuperior/aljabr/blob/main/docs/guides/ui/dom.md Initialize the aljabr renderer with the DOM host. This provides a mount function to render UI trees. ```ts import { createRenderer, view } from "aljabr/ui"; import { domHost } from "aljabr/ui/dom"; const { mount } = createRenderer(domHost); ``` -------------------------------- ### Use localStorageAdapter Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/persist.md Demonstrates using the default localStorageAdapter with persistedSignal. This adapter is backed by window.localStorage. ```typescript import { localStorageAdapter } from "aljabr/prelude" persistedSignal("default", { key: "my.key", adapter: localStorageAdapter, // this is the default; optional }) ``` -------------------------------- ### Function matcher example Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/match.md A simple function matcher is called directly with the variant value. This is the most straightforward way to handle a variant. ```typescript match(result, { Ok: ({ value }) => `got ${value}`, Err: ({ message }) => `error: ${message}`, }) ``` -------------------------------- ### Testing with Manual Flushing Source: https://github.com/jasuperior/aljabr/blob/main/docs/guides/advanced/renderer-protocol.md Demonstrates how to test components using a renderer protocol by manually controlling the flush mechanism, allowing for deterministic assertions. ```ts let flush: (() => void) | null = null; const { mount } = createRenderer(host, { scheduleFlush(f) { flush = f; }, }); const sig = Signal.create("hello"); mount(() => view("p", null, () => sig.get()), host.root); sig.set("world"); // DOM not updated yet — flush hasn't been called flush!(); // DOM now reflects "world" ``` -------------------------------- ### ExactMatchers example Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/match.md Use `ExactMatchers` to ensure all variants of a union type are handled. No `[__]` catch-all is allowed or needed in this mode. ```typescript const Shape = union({ Circle: (radius: number) => ({ radius }), Rect: (w: number, h: number) => ({ w, h }), }) type Shape = Union match(shape, { Circle: ({ radius }) => Math.PI * radius ** 2, Rect: ({ w, h }) => w * h, }) ``` -------------------------------- ### Reactive Shape Editor Example Source: https://github.com/jasuperior/aljabr/blob/main/README.md Demonstrates defining a union type `Shape`, implementing an `area` function using exhaustive `match`, managing a reactive list of shapes with `Ref.create`, calculating a derived total area with `Derived.create`, and rendering a dynamic list with keyed updates using `DerivedArray.map`. It also shows how to create a reactive UI component with `createRenderer` and `domHost`. ```tsx /** @jsxImportSource aljabr/ui */ import { union, match, type Union } from "aljabr"; import { Ref, Derived } from "aljabr/prelude"; import { createRenderer } from "aljabr/ui"; import { domHost } from "aljabr/ui/dom"; const Shape = union({ Circle: (id: number, radius: number) => ({ id, radius }), Rect: (id: number, w: number, h: number) => ({ id, w, h }), }); type Shape = Union; const area = (s: Shape) => match(s, { Circle: ({ radius }) => Math.PI * radius ** 2, Rect: ({ w, h }) => w * h, }); const shapes = Ref.create([Shape.Circle(1, 5), Shape.Rect(2, 3, 4)]); const total = Derived.create(() => shapes.reduce((sum, s) => sum + area(s), 0)); const rows = shapes.map( s =>

  • {area(s).toFixed(2)}
  • , { key: s => s.id }, ); const { mount } = createRenderer(domHost); mount(() =>
      {rows}

    Total: {() => total.get()?.toFixed(2)}

    , document.body, ); ``` -------------------------------- ### peek(): T[] Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/derived-array.md Returns an untracked snapshot of the entire array without registering any reactive dependency. Similar to `get()` but wrapped in `untrack()`. ```APIDOC ## peek(): T[] Returns an untracked snapshot of the entire array. Does not register any reactive dependency. Mirrors `get()` but wrapped in `untrack()` — consistent with `Signal.peek()`. ```ts const evens = Ref.create([1, 2, 3, 4, 5]).filter(x => x % 2 === 0); evens.peek(); // [2, 4] — no dependency registered ``` ``` -------------------------------- ### Event Bubbling Example Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/ui/canvas.md Demonstrates how to use onClick handlers on nested elements and how event.stopPropagation() can prevent an event from bubbling up to parent handlers. ```tsx console.log("group:", e.target.tag)}> { console.log("rect clicked"); e.stopPropagation(); // halts the bubble — group's onClick won't fire }} /> ``` -------------------------------- ### Protocol-Layer Escape Hatch with KeyPress Command Source: https://github.com/jasuperior/aljabr/blob/main/docs/roadmap/v0.4.1.md Demonstrates how to create dynamic key bindings by extending ProseCommand with a KeyPress command and implementing an apply arm. This allows for context-specific key behavior, such as inserting a tab in code blocks. ```typescript const ProseCommandWithKeys = ProseCommand.merge({ KeyPress: (key: string, modifiers: string[]) => ({ key, modifiers }), }) const myApply: CommandProtocol>["apply"] = (state, cmd) => match(cmd, { KeyPress: ({ key, modifiers }) => { if (key === "Tab" && state.activeBlockKind === "Code") { return defaultApply(state, ProseCommand.Insert("\t", state.cursor)) } // ... dynamic logic return Validation.Valid({ next: state, inverse: cmd }) }, [__]: (c) => defaultApply(state, c), }) ``` -------------------------------- ### Setting up Table View State with Ref Source: https://github.com/jasuperior/aljabr/blob/main/docs/guides/advanced/reactive-ui.md Initializes a `Ref` to manage structured view state for a data table, including filter, sort, and pagination. Each field is a distinct reactive path. ```typescript import { Ref, Derived, AsyncDerived, batch } from "aljabr/prelude" import type { OrderLifecycle } from "./domain" // from the Union Branching guide type SortField = "orderId" | "confirmedAt" | "status" type SortDir = "asc" | "desc" type TableState = { filter: string sortField: SortField sortDir: SortDir page: number selected: string | null // orderId } const view = Ref.create({ filter: "", sortField: "confirmedAt", sortDir: "desc", page: 1, selected: null, }) ``` -------------------------------- ### Sub-Ref scoping with .at() Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/ref.md Shows how to create a sub-Ref using `.at()` which scopes reads and writes to a specific part of the root Ref. Operations on the sub-Ref are forwarded to the corresponding paths in the root signal map. ```ts const userRef = ref.at("user") as Ref<{ name: string; age: number }> // Reads and writes on userRef forward to the root signal map userRef.get("name" as any) // "Alice" userRef.set("name" as any, "Bob") ref.get("user.name") // "Bob" ``` -------------------------------- ### Pattern matching on DerivedState Source: https://github.com/jasuperior/aljabr/blob/main/docs/api/prelude/derived.md Example of using a match statement to handle different DerivedState variants: Uncomputed, Computed, Stale, and Disposed. ```typescript match(derived.state, { Uncomputed: () => "never computed", Computed: ({ value }) => `fresh: ${value}`, Stale: ({ value }) => `stale (was: ${value}), recomputing...`, Disposed: () => "cleaned up", }) ```