# @kuratchi/js — Full Framework Reference Project: KuratchiJS Package: @kuratchi/js Machine schema: /_assets/kuratchi-js/llms.json Category: Cloudflare Workers-native web framework This file is the complete reference for building apps with @kuratchi/js. It covers routing, template syntax, server actions, Durable Objects, Agents, progressive enhancement, error handling, runtime APIs, and configuration. ## 1) What KuratchiJS is KuratchiJS is a Cloudflare Workers-native framework. It compiles HTML routes in src/routes into .kuratchi output used by Wrangler. Key capabilities: - File-based routing from HTML files - Server-first execution model — top-level The $database/ alias resolves to src/database/. Any tsconfig path alias works. ### Layout file src/routes/layout.html wraps every page. Use where page content renders: My App
## 5) Template syntax ### Interpolation

{title}

{@html bodyHtml}

{@raw trustedHtml}

### Conditionals (inline JavaScript if/else) if (items.length === 0) {

Nothing here yet.

} else {

{items.length} items

} if (user.role === 'admin') { Admin panel } ### Loops (inline JavaScript for) for (const item of items) {
  • {item.title}
  • } for (const [i, item] of items.entries()) { {i + 1} {item.title} } ### Components Import .html components from src/lib/ or packages: Live ### Client reactivity ($:) Inside client/browser Block form: Rules: - $: is the only browser-side execution primitive in a route template - Runs in browser scripts in template markup, NOT in the top server
    The action function receives raw FormData: import { ActionError } from '@kuratchi/js'; export async function addItem(formData: FormData): Promise { const title = (formData.get('title') as string)?.trim(); if (!title) throw new ActionError('Title is required'); // write to DB... } ### Redirect after action import { redirect } from '@kuratchi/js'; export async function createItem(formData: FormData): Promise { const id = await db.items.insert({ title: formData.get('title') }); redirect(`/items/${id}`); } ## 7) Error handling ### Action errors Throw ActionError for user-facing validation messages: import { ActionError } from '@kuratchi/js'; export async function signIn(formData: FormData) { const email = formData.get('email') as string; const password = formData.get('password') as string; if (!email || !password) throw new ActionError('Email and password are required'); } In the template, the action's state object is available under its function name:
    (signIn.error ? `

    ${signIn.error}

    ` : '')
    State shape: { error?: string, loading: boolean, success: boolean } - actionName.error — set on ActionError throw, cleared on next success - actionName.loading — set by client bridge during submission - actionName.success — reserved for future use Throwing a plain Error shows a generic "Action failed" message in production. ### Load errors (PageError) Throw PageError from a route's load scope: import { PageError } from '@kuratchi/js'; const post = await db.posts.findOne({ id: params.id }); if (!post) throw new PageError(404); if (!post.isPublished && !currentUser?.isAdmin) throw new PageError(403); PageError renders matching custom error pages (src/routes/404.html, etc.) or a built-in fallback. throw new PageError(404); throw new PageError(403, 'Admin only'); throw new PageError(401, 'Login required'); ## 8) Progressive enhancement ### data-action — fetch action (no page reload) The action function receives the args array as individual arguments: export async function deleteItem(id: number): Promise { await db.items.delete({ id }); } ### data-refresh — partial refresh
    for (const item of items) {
    {item.title}
    }
    ### data-get — client-side navigation
    Click to navigate
    ### data-poll — polling
    {status}
    ### data-select-all / data-select-item — checkbox groups for (const todo of todos) { } ## 9) Durable Object RPC ### Function mode (recommended) Create .do.ts files. Exported functions become RPC methods. Use this.db, this.env, and this.ctx inside those functions. // src/server/auth/auth.do.ts import { redirect } from '@kuratchi/js'; export async function getOrgUsers() { const result = await this.db.users.orderBy({ createdAt: 'asc' }).many(); return result.data ?? []; } export async function createOrgUser(formData: FormData) { const email = String(formData.get('email') ?? '').trim().toLowerCase(); if (!email) throw new Error('Email is required'); await this.db.users.insert({ email, role: 'member' }); redirect('/settings/users'); } Import RPC methods from $durable-objects/:
    Optional lifecycle exports (not exposed as RPC): - export async function onInit() - export async function onAlarm(...args) - export function onMessage(...args) ### Class mode (optional) import { kuratchiDO } from '@kuratchi/js'; export default class NotesDO extends kuratchiDO { static binding = 'NOTES_DO'; async getNotes() { return (await this.db.notes.orderBy({ created_at: 'desc' }).many()).data ?? []; } } Declare in kuratchi.config.ts and wrangler.jsonc. The compiler exports DO classes from .kuratchi/worker.js. ## 10) Agents Kuratchi treats src/server/**/*.agent.ts as first-class Worker exports. - The file must export a class (named or default) - The compiler re-exports it from .kuratchi/worker.js - .agent.ts files are NOT route modules and NOT converted into $durable-objects/* proxies // src/server/ai/session.agent.ts import { Agent } from 'agents'; export class SessionAgent extends Agent { async onRequest() { return Response.json({ ok: true }); } } Wrangler config needed: { "durable_objects": { "bindings": [{ "name": "AI_SESSION", "class_name": "SessionAgent" }] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": ["SessionAgent"] } ] } ## 11) Runtime APIs Available anywhere in server-side route code: import { getCtx, // ExecutionContext getRequest, // Request getLocals, // mutable locals bag for the current request getParams, // URL params ({ slug: 'foo' }) getParam, // getParam('slug') redirect, // redirect('/path', 302) goto, // same as redirect — alias RedirectError, // redirect signal thrown by redirect() } from '@kuratchi/js'; ### Request helpers import { url, pathname, searchParams, slug } from '@kuratchi/js/request'; const page = pathname; const tab = searchParams.get('tab'); const postSlug = slug; Exports: url, pathname, searchParams, slug, headers, method, params ### Framework environment import { dev } from '@kuratchi/js/environment'; - dev is true for development builds, false for production - Compile-time framework state, not a process env var ### Environment bindings import { env } from 'cloudflare:workers'; const turnstileSiteKey = env.TURNSTILE_SITE_KEY || ''; Server only. Templates and client

    Hello KuratchiJS

    if (items.length > 0) {
      for (const item of items) {
    • {item}
    • }
    } else {

    No items yet.

    } ### Run bun install bun run build bun run dev ## 15) LLM generation checklist When generating a Kuratchi app, ensure: - src/routes/layout.html exists and contains - src/routes/page.html exists - wrangler main is .kuratchi/worker.js - package scripts include build/dev via kuratchi CLI - Template uses inline JavaScript if/for (not #if or {#each}) - Interpolation uses {expression} (not {{ }}) - bun run build succeeds If user asks for "basic site", generate: - layout.html with nav and slot - home page with template examples (if/for) - one extra route (/about) - one form action example