-
-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathuse-function.ts
More file actions
91 lines (77 loc) · 2.82 KB
/
use-function.ts
File metadata and controls
91 lines (77 loc) · 2.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license.
/**
* React integration for @eserstack/functions — run handlers inside
* React Server Components with proper context injection.
*
* @example
* ```tsx
* import { runFunction } from "@eserstack/laroux-react/use-function";
* import { listRecipes } from "@eserstack/registry/handlers/list-recipes";
*
* export default async function RecipesPage({ params }) {
* const { recipes } = await runFunction(listRecipes({ language: params.lang }));
* return <div>{recipes.map(r => <p>{r.name}</p>)}</div>;
* }
* ```
*
* @module
*/
import * as task from "@eserstack/functions/task";
import * as results from "@eserstack/primitives/results";
import * as streams from "@eserstack/streams";
// =============================================================================
// Types
// =============================================================================
/**
* Minimal context for handlers running inside React.
* Uses plain renderer + buffer sink — React components handle their own
* rendering via SpanView, not through the Output stream.
*/
type FunctionContext = {
readonly out: streams.Output;
};
type RunFunctionOptions = {
/** Additional context properties to merge */
readonly context?: Readonly<Record<string, unknown>>;
};
// =============================================================================
// Core
// =============================================================================
/**
* Run an @eserstack/functions Task inside a React Server Component.
*
* Creates a buffer-based Output context so handlers can write to
* `ctx.out` without side effects. The handler's return value is
* extracted and returned — if the Task fails, an error is thrown
* (React Server Components handle errors via Error Boundaries).
*
* @param t - A Task from an @eserstack/functions handler
* @param options - Optional additional context
* @returns The handler's success value
* @throws Error if the Task fails
*/
const runFunction = async <T, E>(
t: task.Task<T, E, FunctionContext>,
options?: RunFunctionOptions,
): Promise<T> => {
const buf = streams.sinks.buffer();
const out = streams.output({
renderer: streams.renderers.plain(),
sink: buf,
});
const ctx: FunctionContext = { out, ...options?.context };
const result = await task.runTask(t, ctx);
await out.close();
if (results.isOk(result)) {
return result.value;
}
// React Server Components use Error Boundaries for error handling
const error = result.error;
const message = typeof error === "object" && error !== null &&
"message" in error
? String((error as Record<string, unknown>)["message"])
: String(error);
throw new Error(message);
};
export { runFunction };
export type { FunctionContext, RunFunctionOptions };