lemma-sdk 0.2.30 → 0.2.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +213 -51
  2. package/dist/react/index.d.ts +23 -1
  3. package/dist/react/index.js +11 -0
  4. package/dist/react/useAgentInputSchema.d.ts +19 -0
  5. package/dist/react/useAgentInputSchema.js +73 -0
  6. package/dist/react/useAgentRun.js +18 -20
  7. package/dist/react/useAgentRuns.d.ts +33 -0
  8. package/dist/react/useAgentRuns.js +149 -0
  9. package/dist/react/useAssistantRun.js +10 -9
  10. package/dist/react/useAssistantSession.js +21 -25
  11. package/dist/react/useBulkRecords.js +9 -16
  12. package/dist/react/useConversation.js +24 -8
  13. package/dist/react/useConversations.d.ts +4 -0
  14. package/dist/react/useConversations.js +49 -3
  15. package/dist/react/useCreateRecord.d.ts +33 -3
  16. package/dist/react/useCreateRecord.js +20 -17
  17. package/dist/react/useCurrentUser.d.ts +14 -0
  18. package/dist/react/useCurrentUser.js +68 -0
  19. package/dist/react/useDeleteRecord.js +9 -16
  20. package/dist/react/useFlowRunHistory.js +1 -5
  21. package/dist/react/useFlowSession.js +41 -33
  22. package/dist/react/useForeignKeyOptions.d.ts +18 -0
  23. package/dist/react/useForeignKeyOptions.js +26 -15
  24. package/dist/react/useFunctionRun.d.ts +36 -0
  25. package/dist/react/useFunctionRun.js +30 -0
  26. package/dist/react/useFunctionRuns.d.ts +33 -0
  27. package/dist/react/useFunctionRuns.js +149 -0
  28. package/dist/react/useFunctionSession.js +37 -29
  29. package/dist/react/useJoinedRecords.d.ts +57 -2
  30. package/dist/react/useJoinedRecords.js +77 -27
  31. package/dist/react/useMembers.d.ts +4 -0
  32. package/dist/react/useMembers.js +55 -16
  33. package/dist/react/useOrganizationMembers.d.ts +26 -0
  34. package/dist/react/useOrganizationMembers.js +113 -0
  35. package/dist/react/usePodAccess.d.ts +22 -0
  36. package/dist/react/usePodAccess.js +128 -0
  37. package/dist/react/useRecord.d.ts +16 -0
  38. package/dist/react/useRecord.js +24 -13
  39. package/dist/react/useRecordForm.d.ts +42 -3
  40. package/dist/react/useRecordForm.js +44 -24
  41. package/dist/react/useRecords.d.ts +2 -0
  42. package/dist/react/useRecords.js +62 -22
  43. package/dist/react/useReferencingRecords.d.ts +66 -0
  44. package/dist/react/useReferencingRecords.js +159 -0
  45. package/dist/react/useRelatedRecords.d.ts +17 -0
  46. package/dist/react/useRelatedRecords.js +28 -21
  47. package/dist/react/useReverseRelatedRecords.d.ts +21 -0
  48. package/dist/react/useReverseRelatedRecords.js +30 -21
  49. package/dist/react/useSchemaForm.js +1 -13
  50. package/dist/react/useTable.js +24 -13
  51. package/dist/react/useTables.d.ts +4 -0
  52. package/dist/react/useTables.js +57 -15
  53. package/dist/react/useTaskSession.js +11 -22
  54. package/dist/react/useUpdateRecord.d.ts +34 -3
  55. package/dist/react/useUpdateRecord.js +21 -17
  56. package/dist/react/useWorkflowResume.d.ts +18 -0
  57. package/dist/react/useWorkflowResume.js +45 -0
  58. package/dist/react/useWorkflowRun.d.ts +21 -0
  59. package/dist/react/useWorkflowRun.js +49 -0
  60. package/dist/react/useWorkflowRuns.d.ts +33 -0
  61. package/dist/react/useWorkflowRuns.js +149 -0
  62. package/dist/react/useWorkflowStart.js +20 -27
  63. package/dist/react/utils.d.ts +5 -0
  64. package/dist/react/utils.js +25 -0
  65. package/dist/types.d.ts +1 -0
  66. package/package.json +2 -4
  67. package/dist/react/components/AssistantChrome.d.ts +0 -86
  68. package/dist/react/components/AssistantChrome.js +0 -48
  69. package/dist/react/components/AssistantEmbedded.d.ts +0 -10
  70. package/dist/react/components/AssistantEmbedded.js +0 -15
  71. package/dist/react/components/AssistantExperience.d.ts +0 -96
  72. package/dist/react/components/AssistantExperience.js +0 -1294
  73. package/dist/react/components/assistant-types.d.ts +0 -80
  74. package/dist/react/components/assistant-types.js +0 -1
@@ -1,21 +1,12 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
- function resolvePodClient(client, podId) {
3
- if (!podId || podId === client.podId)
4
- return client;
5
- return client.withPod(podId);
6
- }
7
- function normalizeError(error, fallback) {
8
- if (error instanceof Error)
9
- return error;
10
- return new Error(fallback);
11
- }
2
+ import { normalizeError, resolvePodClient } from "./utils.js";
12
3
  export function useTable({ client, podId, tableName, enabled = true, autoLoad = true, }) {
13
4
  const [table, setTable] = useState(null);
14
5
  const [isLoading, setIsLoading] = useState(false);
15
6
  const [error, setError] = useState(null);
16
7
  const trimmedTableName = tableName.trim();
17
8
  const isEnabled = enabled && trimmedTableName.length > 0;
18
- const refresh = useCallback(async () => {
9
+ const refresh = useCallback(async (signal) => {
19
10
  if (!isEnabled) {
20
11
  setTable(null);
21
12
  setError(null);
@@ -27,16 +18,21 @@ export function useTable({ client, podId, tableName, enabled = true, autoLoad =
27
18
  try {
28
19
  const scopedClient = resolvePodClient(client, podId);
29
20
  const nextTable = await scopedClient.tables.get(trimmedTableName);
21
+ if (signal?.aborted)
22
+ return null;
30
23
  setTable(nextTable);
31
24
  return nextTable;
32
25
  }
33
26
  catch (refreshError) {
27
+ if (signal?.aborted)
28
+ return null;
34
29
  const normalized = normalizeError(refreshError, "Failed to load table.");
35
30
  setError(normalized);
36
31
  return null;
37
32
  }
38
33
  finally {
39
- setIsLoading(false);
34
+ if (!signal?.aborted)
35
+ setIsLoading(false);
40
36
  }
41
37
  }, [client, isEnabled, podId, trimmedTableName]);
42
38
  useEffect(() => {
@@ -48,7 +44,22 @@ export function useTable({ client, podId, tableName, enabled = true, autoLoad =
48
44
  }
49
45
  if (!autoLoad)
50
46
  return;
51
- void refresh();
47
+ const controller = new AbortController();
48
+ let cancelled = false;
49
+ (async () => {
50
+ try {
51
+ await refresh(controller.signal);
52
+ }
53
+ catch {
54
+ if (!cancelled) {
55
+ setError(normalizeError(new Error("Failed to load table."), "Failed to load table."));
56
+ }
57
+ }
58
+ })();
59
+ return () => {
60
+ cancelled = true;
61
+ controller.abort();
62
+ };
52
63
  }, [autoLoad, isEnabled, refresh]);
53
64
  return useMemo(() => ({
54
65
  table,
@@ -13,10 +13,14 @@ export interface UseTablesResult {
13
13
  total: number;
14
14
  nextPageToken: string | null;
15
15
  isLoading: boolean;
16
+ isLoadingMore: boolean;
16
17
  error: Error | null;
17
18
  refresh: (overrides?: {
18
19
  limit?: number;
19
20
  pageToken?: string;
20
21
  }) => Promise<Table[]>;
22
+ loadMore: (overrides?: {
23
+ limit?: number;
24
+ }) => Promise<Table[]>;
21
25
  }
22
26
  export declare function useTables({ client, podId, enabled, autoLoad, limit, pageToken, }: UseTablesOptions): UseTablesResult;
@@ -1,21 +1,13 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
- function resolvePodClient(client, podId) {
3
- if (!podId || podId === client.podId)
4
- return client;
5
- return client.withPod(podId);
6
- }
7
- function normalizeError(error, fallback) {
8
- if (error instanceof Error)
9
- return error;
10
- return new Error(fallback);
11
- }
2
+ import { normalizeError, resolvePodClient } from "./utils.js";
12
3
  export function useTables({ client, podId, enabled = true, autoLoad = true, limit = 100, pageToken, }) {
13
4
  const [tables, setTables] = useState([]);
14
5
  const [total, setTotal] = useState(0);
15
6
  const [nextPageToken, setNextPageToken] = useState(null);
16
7
  const [isLoading, setIsLoading] = useState(false);
8
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
17
9
  const [error, setError] = useState(null);
18
- const refresh = useCallback(async (overrides = {}) => {
10
+ const refresh = useCallback(async (overrides = {}, signal) => {
19
11
  if (!enabled) {
20
12
  setTables([]);
21
13
  setTotal(0);
@@ -32,21 +24,53 @@ export function useTables({ client, podId, enabled = true, autoLoad = true, limi
32
24
  limit: overrides.limit ?? limit,
33
25
  pageToken: overrides.pageToken ?? pageToken,
34
26
  });
27
+ if (signal?.aborted)
28
+ return [];
35
29
  const nextTables = response.items ?? [];
36
30
  setTables(nextTables);
37
- setTotal(nextTables.length);
31
+ setTotal(response.total ?? nextTables.length);
38
32
  setNextPageToken(response.next_page_token ?? null);
39
33
  return nextTables;
40
34
  }
41
35
  catch (refreshError) {
36
+ if (signal?.aborted)
37
+ return [];
42
38
  const normalized = normalizeError(refreshError, "Failed to load tables.");
43
39
  setError(normalized);
44
40
  return [];
45
41
  }
46
42
  finally {
47
- setIsLoading(false);
43
+ if (!signal?.aborted)
44
+ setIsLoading(false);
48
45
  }
49
46
  }, [client, enabled, limit, pageToken, podId]);
47
+ const loadMore = useCallback(async (overrides = {}) => {
48
+ if (!enabled || !nextPageToken || isLoading || isLoadingMore) {
49
+ return [];
50
+ }
51
+ setIsLoadingMore(true);
52
+ setError(null);
53
+ try {
54
+ const scopedClient = resolvePodClient(client, podId);
55
+ const response = await scopedClient.tables.list({
56
+ limit: overrides.limit ?? limit,
57
+ pageToken: nextPageToken,
58
+ });
59
+ const moreTables = response.items ?? [];
60
+ setTables((previous) => [...previous, ...moreTables]);
61
+ setTotal(response.total ?? tables.length + moreTables.length);
62
+ setNextPageToken(response.next_page_token ?? null);
63
+ return moreTables;
64
+ }
65
+ catch (loadError) {
66
+ const normalized = normalizeError(loadError, "Failed to load more tables.");
67
+ setError(normalized);
68
+ return [];
69
+ }
70
+ finally {
71
+ setIsLoadingMore(false);
72
+ }
73
+ }, [client, enabled, isLoading, isLoadingMore, limit, nextPageToken, podId, tables.length]);
50
74
  useEffect(() => {
51
75
  if (!enabled) {
52
76
  setTables([]);
@@ -54,18 +78,36 @@ export function useTables({ client, podId, enabled = true, autoLoad = true, limi
54
78
  setNextPageToken(null);
55
79
  setError(null);
56
80
  setIsLoading(false);
81
+ setIsLoadingMore(false);
57
82
  return;
58
83
  }
59
84
  if (!autoLoad)
60
85
  return;
61
- void refresh();
86
+ const controller = new AbortController();
87
+ let cancelled = false;
88
+ (async () => {
89
+ try {
90
+ await refresh({}, controller.signal);
91
+ }
92
+ catch {
93
+ if (!cancelled) {
94
+ setError(normalizeError(new Error("Failed to load tables."), "Failed to load tables."));
95
+ }
96
+ }
97
+ })();
98
+ return () => {
99
+ cancelled = true;
100
+ controller.abort();
101
+ };
62
102
  }, [autoLoad, enabled, refresh]);
63
103
  return useMemo(() => ({
64
104
  tables,
65
105
  total,
66
106
  nextPageToken,
67
107
  isLoading,
108
+ isLoadingMore,
68
109
  error,
69
110
  refresh,
70
- }), [error, isLoading, nextPageToken, refresh, tables, total]);
111
+ loadMore,
112
+ }), [error, isLoading, isLoadingMore, loadMore, nextPageToken, refresh, tables, total]);
71
113
  }
@@ -2,18 +2,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { parseSSEJson, readSSE } from "../streams.js";
3
3
  import { isTerminalTaskStatus, normalizeRunStatus } from "../run-utils.js";
4
4
  import { parseTaskStreamEvent, upsertTaskMessage } from "../task-events.js";
5
- function resolvePodId(client, podId) {
6
- const resolved = podId ?? client.podId;
7
- if (!resolved) {
8
- throw new Error("podId is required. Pass podId or set it on LemmaClient.");
9
- }
10
- return resolved;
11
- }
12
- function normalizeError(error, fallback) {
13
- if (error instanceof Error)
14
- return error;
15
- return new Error(fallback);
16
- }
5
+ import { normalizeError, resolvePodClient, resolvePodId } from "./utils.js";
17
6
  function sleep(ms) {
18
7
  return new Promise((resolve) => setTimeout(resolve, ms));
19
8
  }
@@ -92,8 +81,8 @@ export function useTaskSession({ client, podId, taskId: externalTaskId = null, a
92
81
  if (!id)
93
82
  return null;
94
83
  try {
95
- client.setPodId(resolvePodId(client, podId));
96
- const nextTask = await client.tasks.get(id);
84
+ const scopedClient = resolvePodClient(client, resolvePodId(client, podId));
85
+ const nextTask = await scopedClient.tasks.get(id);
97
86
  setTask(nextTask);
98
87
  setTaskStatus(nextTask.status);
99
88
  return nextTask;
@@ -110,8 +99,8 @@ export function useTaskSession({ client, podId, taskId: externalTaskId = null, a
110
99
  if (!id)
111
100
  return [];
112
101
  try {
113
- client.setPodId(resolvePodId(client, podId));
114
- const response = await client.tasks.messages.list(id, { limit: 100 });
102
+ const scopedClient = resolvePodClient(client, resolvePodId(client, podId));
103
+ const response = await scopedClient.tasks.messages.list(id, { limit: 100 });
115
104
  const nextMessages = response.items ?? [];
116
105
  setMessages(nextMessages);
117
106
  return nextMessages;
@@ -141,8 +130,8 @@ export function useTaskSession({ client, podId, taskId: externalTaskId = null, a
141
130
  break;
142
131
  }
143
132
  try {
144
- client.setPodId(resolvePodId(client, podId));
145
- const stream = await client.tasks.stream(id, { signal: controller.signal });
133
+ const scopedClient = resolvePodClient(client, resolvePodId(client, podId));
134
+ const stream = await scopedClient.tasks.stream(id, { signal: controller.signal });
146
135
  reconnectDelayMs = 1000;
147
136
  for await (const event of readSSE(stream)) {
148
137
  if (controller.signal.aborted) {
@@ -201,8 +190,8 @@ export function useTaskSession({ client, podId, taskId: externalTaskId = null, a
201
190
  }, [client, disconnect, podId, setTaskStatus]);
202
191
  const start = useCallback(async (input) => {
203
192
  setError(null);
204
- client.setPodId(resolvePodId(client, podId));
205
- const created = await client.tasks.create({
193
+ const scopedClient = resolvePodClient(client, resolvePodId(client, podId));
194
+ const created = await scopedClient.tasks.create({
206
195
  agent_name: input.agentName,
207
196
  input_data: input.inputData,
208
197
  });
@@ -221,8 +210,8 @@ export function useTaskSession({ client, podId, taskId: externalTaskId = null, a
221
210
  if (!id)
222
211
  return null;
223
212
  try {
224
- client.setPodId(resolvePodId(client, podId));
225
- const stopped = await client.tasks.stop(id);
213
+ const scopedClient = resolvePodClient(client, resolvePodId(client, podId));
214
+ const stopped = await scopedClient.tasks.stop(id);
226
215
  setTask(stopped);
227
216
  setTaskStatus(stopped.status);
228
217
  return stopped;
@@ -1,12 +1,43 @@
1
1
  import type { LemmaClient } from "../client.js";
2
- import type { RecordResponse } from "../types.js";
2
+ import type { FunctionRun, RecordResponse } from "../types.js";
3
+ /**
4
+ * React hook for updating a single record. Manages loading/error state and
5
+ * exposes an `update` function you can call from event handlers.
6
+ *
7
+ * Supports two modes:
8
+ * - `"direct"` (default): calls `records.update` directly.
9
+ * - `"function"`: calls `functions.runs.create`, routing the update through
10
+ * a pod function (e.g. for status transitions that log history).
11
+ *
12
+ * @example Direct update
13
+ * ```tsx
14
+ * const { update, isSubmitting } = useUpdateRecord({ client, tableName: "issues", recordId: "123" });
15
+ * await update({ status: "closed" });
16
+ * ```
17
+ *
18
+ * @example Function-backed update
19
+ * ```tsx
20
+ * const { update, isSubmitting } = useUpdateRecord({
21
+ * client,
22
+ * tableName: "issues",
23
+ * recordId: "123",
24
+ * updateVia: "function",
25
+ * updateFunctionName: "update-issue-status",
26
+ * });
27
+ * await update({ status: "in_progress" });
28
+ * ```
29
+ */
3
30
  export interface UseUpdateRecordOptions {
4
31
  client: LemmaClient;
5
32
  podId?: string;
6
33
  tableName: string;
7
34
  recordId?: string | null;
8
35
  enabled?: boolean;
9
- onSuccess?: (record: Record<string, unknown>, response: RecordResponse) => void;
36
+ /** How the record is updated. `"direct"` calls `records.update`. `"function"` calls `functions.runs.create`. */
37
+ updateVia?: "direct" | "function";
38
+ /** Function name to run when `updateVia` is `"function"`. Falls back to `tableName` if omitted. */
39
+ updateFunctionName?: string;
40
+ onSuccess?: (record: Record<string, unknown>, response: RecordResponse | FunctionRun) => void;
10
41
  onError?: (error: unknown) => void;
11
42
  }
12
43
  export interface UseUpdateRecordResult<TRecord extends Record<string, unknown> = Record<string, unknown>> {
@@ -18,4 +49,4 @@ export interface UseUpdateRecordResult<TRecord extends Record<string, unknown> =
18
49
  }) => Promise<TRecord | null>;
19
50
  reset: () => void;
20
51
  }
21
- export declare function useUpdateRecord<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, recordId, enabled, onSuccess, onError, }: UseUpdateRecordOptions): UseUpdateRecordResult<TRecord>;
52
+ export declare function useUpdateRecord<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, recordId, enabled, updateVia, updateFunctionName, onSuccess, onError, }: UseUpdateRecordOptions): UseUpdateRecordResult<TRecord>;
@@ -1,18 +1,13 @@
1
- import { useCallback, useMemo, useState } from "react";
2
- function resolvePodClient(client, podId) {
3
- if (!podId || podId === client.podId)
4
- return client;
5
- return client.withPod(podId);
6
- }
7
- function normalizeError(error, fallback) {
8
- if (error instanceof Error)
9
- return error;
10
- return new Error(fallback);
11
- }
12
- export function useUpdateRecord({ client, podId, tableName, recordId = null, enabled = true, onSuccess, onError, }) {
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { normalizeError, resolvePodClient } from "./utils.js";
3
+ export function useUpdateRecord({ client, podId, tableName, recordId = null, enabled = true, updateVia = "direct", updateFunctionName, onSuccess, onError, }) {
13
4
  const [updatedRecord, setUpdatedRecord] = useState(null);
14
5
  const [isSubmitting, setIsSubmitting] = useState(false);
15
6
  const [error, setError] = useState(null);
7
+ const onSuccessRef = useRef(onSuccess);
8
+ const onErrorRef = useRef(onError);
9
+ useEffect(() => { onSuccessRef.current = onSuccess; }, [onSuccess]);
10
+ useEffect(() => { onErrorRef.current = onError; }, [onError]);
16
11
  const trimmedTableName = tableName.trim();
17
12
  const trimmedRecordId = typeof recordId === "string" ? recordId.trim() : "";
18
13
  const isEnabled = enabled && trimmedTableName.length > 0;
@@ -21,32 +16,41 @@ export function useUpdateRecord({ client, podId, tableName, recordId = null, ena
21
16
  ? overrides.recordId.trim()
22
17
  : trimmedRecordId;
23
18
  if (!isEnabled || nextRecordId.length === 0) {
24
- const disabledError = new Error("Record update requires a table and record ID.");
25
- setError(disabledError);
26
19
  return null;
27
20
  }
28
21
  setIsSubmitting(true);
29
22
  setError(null);
30
23
  try {
31
24
  const scopedClient = resolvePodClient(client, podId);
25
+ if (updateVia === "function") {
26
+ const functionName = updateFunctionName ?? trimmedTableName;
27
+ const input = { ...data, id: nextRecordId, record_id: nextRecordId };
28
+ const run = await scopedClient.functions.runs.create(functionName, { input });
29
+ const nextRecord = (run.output_data ?? { id: nextRecordId, ...data });
30
+ setUpdatedRecord(nextRecord);
31
+ if (nextRecord) {
32
+ onSuccessRef.current?.(nextRecord, run);
33
+ }
34
+ return nextRecord;
35
+ }
32
36
  const response = await scopedClient.records.update(trimmedTableName, nextRecordId, data);
33
37
  const nextRecord = (response.data ?? null);
34
38
  setUpdatedRecord(nextRecord);
35
39
  if (nextRecord) {
36
- onSuccess?.(nextRecord, response);
40
+ onSuccessRef.current?.(nextRecord, response);
37
41
  }
38
42
  return nextRecord;
39
43
  }
40
44
  catch (mutationError) {
41
45
  const normalized = normalizeError(mutationError, "Failed to update record.");
42
46
  setError(normalized);
43
- onError?.(mutationError);
47
+ onErrorRef.current?.(mutationError);
44
48
  return null;
45
49
  }
46
50
  finally {
47
51
  setIsSubmitting(false);
48
52
  }
49
- }, [client, isEnabled, onError, onSuccess, podId, trimmedRecordId, trimmedTableName]);
53
+ }, [client, isEnabled, podId, trimmedRecordId, trimmedTableName, updateFunctionName, updateVia]);
50
54
  const reset = useCallback(() => {
51
55
  setUpdatedRecord(null);
52
56
  setError(null);
@@ -0,0 +1,18 @@
1
+ import type { LemmaClient } from "../client.js";
2
+ import type { FlowRun, WorkflowRunInputs } from "../types.js";
3
+ export interface UseWorkflowResumeOptions {
4
+ client: LemmaClient;
5
+ podId?: string;
6
+ runId?: string | null;
7
+ onRun?: (run: FlowRun) => void;
8
+ onError?: (error: unknown) => void;
9
+ }
10
+ export interface UseWorkflowResumeResult {
11
+ run: FlowRun | null;
12
+ isResuming: boolean;
13
+ error: Error | null;
14
+ resume: (inputs?: WorkflowRunInputs, options?: {
15
+ runId?: string | null;
16
+ }) => Promise<FlowRun>;
17
+ }
18
+ export declare function useWorkflowResume({ client, podId, runId, onRun, onError, }: UseWorkflowResumeOptions): UseWorkflowResumeResult;
@@ -0,0 +1,45 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { normalizeError, resolvePodId } from "./utils.js";
3
+ function resolveRunId(base, override) {
4
+ const resolved = override ?? base;
5
+ if (!resolved) {
6
+ throw new Error("runId is required.");
7
+ }
8
+ return resolved;
9
+ }
10
+ export function useWorkflowResume({ client, podId, runId, onRun, onError, }) {
11
+ const [run, setRun] = useState(null);
12
+ const [isResuming, setIsResuming] = useState(false);
13
+ const [error, setError] = useState(null);
14
+ const onRunRef = useRef(onRun);
15
+ const onErrorRef = useRef(onError);
16
+ useEffect(() => { onRunRef.current = onRun; }, [onRun]);
17
+ useEffect(() => { onErrorRef.current = onError; }, [onError]);
18
+ const resume = useCallback(async (inputs = {}, options = {}) => {
19
+ setIsResuming(true);
20
+ setError(null);
21
+ try {
22
+ const resolvedPodId = resolvePodId(client, podId);
23
+ const resolvedRunId = resolveRunId(runId, options.runId);
24
+ const nextRun = await client.workflows.runs.resume(resolvedRunId, inputs, resolvedPodId);
25
+ setRun(nextRun);
26
+ onRunRef.current?.(nextRun);
27
+ return nextRun;
28
+ }
29
+ catch (resumeError) {
30
+ const normalized = normalizeError(resumeError, "Failed to resume workflow run.");
31
+ setError(normalized);
32
+ onErrorRef.current?.(resumeError);
33
+ throw normalized;
34
+ }
35
+ finally {
36
+ setIsResuming(false);
37
+ }
38
+ }, [client, podId, runId]);
39
+ return useMemo(() => ({
40
+ run,
41
+ isResuming,
42
+ error,
43
+ resume,
44
+ }), [error, isResuming, resume, run]);
45
+ }
@@ -0,0 +1,21 @@
1
+ import type { FlowRun, WorkflowRunInputs } from "../types.js";
2
+ import { type UseFlowSessionOptions, type UseFlowSessionResult } from "./useFlowSession.js";
3
+ export interface UseWorkflowRunOptions extends Omit<UseFlowSessionOptions, "flowName"> {
4
+ workflowName?: string;
5
+ }
6
+ export interface UseWorkflowRunResult extends Omit<UseFlowSessionResult, "start" | "listHistory"> {
7
+ output: FlowRun["execution_context"] | null;
8
+ finalOutput: FlowRun["execution_context"] | null;
9
+ isWaitingForInput: boolean;
10
+ isFinished: boolean;
11
+ start: (inputs?: WorkflowRunInputs, options?: {
12
+ workflowName?: string;
13
+ connect?: boolean;
14
+ }) => Promise<FlowRun>;
15
+ listRuns: (options?: {
16
+ workflowName?: string;
17
+ limit?: number;
18
+ pageToken?: string;
19
+ }) => Promise<FlowRun[]>;
20
+ }
21
+ export declare function useWorkflowRun({ workflowName, ...options }: UseWorkflowRunOptions): UseWorkflowRunResult;
@@ -0,0 +1,49 @@
1
+ import { useCallback, useMemo } from "react";
2
+ import { isTerminalFlowStatus, normalizeRunStatus } from "../run-utils.js";
3
+ import { useFlowSession, } from "./useFlowSession.js";
4
+ function resolveWorkflowName(base, override) {
5
+ const resolved = override ?? base;
6
+ if (!resolved) {
7
+ throw new Error("workflowName is required.");
8
+ }
9
+ return resolved;
10
+ }
11
+ export function useWorkflowRun({ workflowName, ...options }) {
12
+ const session = useFlowSession({
13
+ ...options,
14
+ flowName: workflowName,
15
+ });
16
+ const start = useCallback(async (inputs, startOptions = {}) => {
17
+ return session.start({
18
+ flowName: resolveWorkflowName(workflowName, startOptions.workflowName),
19
+ inputs,
20
+ connect: startOptions.connect,
21
+ });
22
+ }, [session, workflowName]);
23
+ const listRuns = useCallback(async (listOptions = {}) => {
24
+ return session.listHistory({
25
+ flowName: resolveWorkflowName(workflowName, listOptions.workflowName),
26
+ limit: listOptions.limit,
27
+ pageToken: listOptions.pageToken,
28
+ });
29
+ }, [session, workflowName]);
30
+ return useMemo(() => {
31
+ const normalizedStatus = normalizeRunStatus(session.status);
32
+ const isFinished = isTerminalFlowStatus(normalizedStatus);
33
+ const isWaitingForInput = normalizedStatus === "WAITING"
34
+ || !!session.run?.waiting_task_id
35
+ || !!session.run?.waiting_function_run_id
36
+ || !!session.run?.waiting_trigger_id;
37
+ const output = session.run?.execution_context ?? null;
38
+ const finalOutput = isFinished ? output : null;
39
+ return {
40
+ ...session,
41
+ output,
42
+ finalOutput,
43
+ isWaitingForInput,
44
+ isFinished,
45
+ start,
46
+ listRuns,
47
+ };
48
+ }, [listRuns, session, start]);
49
+ }
@@ -0,0 +1,33 @@
1
+ import type { LemmaClient } from "../client.js";
2
+ import type { FlowRun } from "../types.js";
3
+ export interface UseWorkflowRunsOptions {
4
+ client: LemmaClient;
5
+ podId?: string;
6
+ workflowName: string;
7
+ enabled?: boolean;
8
+ autoLoad?: boolean;
9
+ limit?: number;
10
+ pageToken?: string;
11
+ initialRunId?: string | null;
12
+ }
13
+ export interface UseWorkflowRunsResult {
14
+ runs: FlowRun[];
15
+ total: number;
16
+ nextPageToken: string | null;
17
+ selectedRunId: string | null;
18
+ effectiveSelectedRunId: string | null;
19
+ selectedRun: FlowRun | null;
20
+ isLoading: boolean;
21
+ isLoadingMore: boolean;
22
+ error: Error | null;
23
+ selectRun: (runId: string | null) => void;
24
+ clearSelection: () => void;
25
+ refresh: (overrides?: {
26
+ limit?: number;
27
+ pageToken?: string;
28
+ }) => Promise<FlowRun[]>;
29
+ loadMore: (overrides?: {
30
+ limit?: number;
31
+ }) => Promise<FlowRun[]>;
32
+ }
33
+ export declare function useWorkflowRuns({ client, podId, workflowName, enabled, autoLoad, limit, pageToken, initialRunId, }: UseWorkflowRunsOptions): UseWorkflowRunsResult;