원클릭으로
huuma-ui-remote-functions
Adding server-side mutations and form handling to a Huuma UI app with type-safe .remote.ts functions.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Adding server-side mutations and form handling to a Huuma UI app with type-safe .remote.ts functions.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Diagnosing and fixing Huuma UI lint violations and runtime errors after they occur.
Editing root.tsx, entry points, dev/prod workflow, environment variables, and the Huuma UI build pipeline.
Building or navigating a Huuma UI app — file conventions, server/client boundary rules, dev workflow, and where to look for authoritative answers.
Adding translations and multilingual routing to a Huuma UI app with setupI18n, useI18n middleware, and the T component.
Adding client interactivity to a Huuma UI app with `.client.tsx` islands, on-* events, refs, and hydration boundaries.
Adding Huuma UI pages, layouts, routes, dynamic params, data loading with resolvers, metadata, and middleware.
| name | huuma-ui-remote-functions |
| description | Adding server-side mutations and form handling to a Huuma UI app with type-safe .remote.ts functions. |
Use this skill when the agent is adding server-side mutations, form submissions, or backend calls in a Huuma UI app.
.remote.ts files.export async function name(...) (lint async-remote-functions).default remote function.export const foo = ... and export { foo } are not reliably recognized by the current bundler stub; prefer named export async function..remote.ts file into an island (.client.tsx).fetch to POST /_huuma/remote/<hash>/<name>.void / undefined resolves to undefined on the client (the server responds 204 No Content). This is the correct way to express "fire and forget" or "acknowledged mutation" — do not return undefined accidentally.Error carrying name and message from the server. Wrap calls in try/catch.The server (packRemoteFunctions in src/platform/server/pack/pack.ts) normalizes the return before sending:
| Return value | Server response | Client receives |
|---|---|---|
undefined (or void) | 204 No Content | undefined |
null | 200 body null | null |
| Any JSON-serializable value | 200 body JSON.stringify(res) | the deserialized value |
function, Symbol, Symbol-keyed-only object | 500 RemoteFunctionSerializationError | rejected Error |
BigInt, circular reference (throws in JSON.stringify) | 500 RemoteFunctionSerializationError | rejected Error |
The void/null distinction is preserved end-to-end: void → undefined, explicit null → null.
Current behavior (the success path is raw JSON; the error path uses HTTP status + a small JSON body):
2xx): the client stub parses the body as JSON and resolves. The success body shape is not enveloped — JSON.stringify(res) is sent directly. Backward compatible.204: the client stub resolves undefined without parsing.2xx (including 500 from a thrown remote function or an unserializable return): the client stub reads the JSON body and throws a reconstructed Error with:
err.name ← body.name (e.g. "RemoteFunctionSerializationError"), falling back to "RemoteFunctionError".err.message ← body.message, falling back to "Remote function \"<name>\" failed (HTTP <status>)".502, HTML error page), the fallback message still carries the status code.name only survives for serialization errors. The only server path that emits a name field is the unserializable-return 500 produced inside executeRemoteFunction. Every other error — a user-thrown error inside the remote function, NotFoundException (function not found), and @huuma/validate body-schema failures — propagates to the framework's global handleException (@huuma/route), which emits { status, message, error? } with no name. So for all real application errors the client's err.name falls back to the generic "RemoteFunctionError"; only err.message carries useful information. Closing this gap (wrapping user-thrown errors with a structured { name, message, ... } body before they reach handleException) is the first Round 1 work item.TypeError: Failed to fetch and are not wrapped into a RemoteFunctionError yet. This is a known gap.The error body today carries only { name, message }. Fields like stack, cause, code, or validation fields are not yet transported. Treat the body shape as provisional.
@huuma/validate inside the remote-handler shipped by @huuma/ui/server/pack.{ remoteFunction: string, args?: unknown[] }.remoteFunction on the imported module and invokes it with the args.remoteFunctionsSchema.parse(body) throws and is handled by the framework's global error handler — not yet by the structured error path above. The same applies to NotFoundException ("Remote function not found"). Both are open improvements; do not rely on a structured body for these yet.Error — its name and message will reach the client.PUBLIC_ env vars are shimmed to the client.name asymmetry: the only server path that emits a name field is the unserializable-return 500 from executeRemoteFunction. Every other error (user-thrown, NotFoundException, @huuma/validate schema failure) goes through the framework's global handleException in @huuma/route, which emits { status, message, error? } with no name. So all real application errors collapse to the generic "RemoteFunctionError" on the client; only message survives. Wrapping user-thrown errors with { name, message, ... } is the first Round 1 work item.TypeError: Failed to fetch) into RemoteFunctionError; callers see the raw browser error.name and message (and only name on the serialization-error path). stack, cause, custom subclass properties, and validation fields are not yet transported. Dev/prod redaction policy for stack is not yet defined.Error with name/message set — there is no instanceof-checkable RemoteFunctionError class exported from @huuma/ui yet.improvements.md.export async function.void/undefined intentionally. Watch for accidental BigInt, function, Symbol, or circular references — they produce a 500 RemoteFunctionSerializationError..remote.ts file is imported only from islands (or server-safe code), not from server components that will ship to the client.try/catch that handles the reconstructed Error (use error.name to distinguish RemoteFunctionSerializationError from your own thrown errors).undefined for void returns, not null.