lemma-sdk 0.2.30 → 0.2.31

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 (64) hide show
  1. package/README.md +211 -51
  2. package/dist/react/index.d.ts +20 -0
  3. package/dist/react/index.js +10 -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.js +9 -16
  16. package/dist/react/useCurrentUser.d.ts +14 -0
  17. package/dist/react/useCurrentUser.js +68 -0
  18. package/dist/react/useDeleteRecord.js +9 -16
  19. package/dist/react/useFlowRunHistory.js +1 -5
  20. package/dist/react/useFlowSession.js +41 -33
  21. package/dist/react/useForeignKeyOptions.js +26 -15
  22. package/dist/react/useFunctionRun.d.ts +19 -0
  23. package/dist/react/useFunctionRun.js +30 -0
  24. package/dist/react/useFunctionRuns.d.ts +33 -0
  25. package/dist/react/useFunctionRuns.js +149 -0
  26. package/dist/react/useFunctionSession.js +37 -29
  27. package/dist/react/useJoinedRecords.js +24 -23
  28. package/dist/react/useMembers.d.ts +4 -0
  29. package/dist/react/useMembers.js +55 -16
  30. package/dist/react/useOrganizationMembers.d.ts +26 -0
  31. package/dist/react/useOrganizationMembers.js +113 -0
  32. package/dist/react/usePodAccess.d.ts +22 -0
  33. package/dist/react/usePodAccess.js +128 -0
  34. package/dist/react/useRecord.js +24 -13
  35. package/dist/react/useRecordForm.js +1 -18
  36. package/dist/react/useRecords.d.ts +2 -0
  37. package/dist/react/useRecords.js +62 -22
  38. package/dist/react/useRelatedRecords.js +28 -21
  39. package/dist/react/useReverseRelatedRecords.js +30 -21
  40. package/dist/react/useSchemaForm.js +1 -13
  41. package/dist/react/useTable.js +24 -13
  42. package/dist/react/useTables.d.ts +4 -0
  43. package/dist/react/useTables.js +57 -15
  44. package/dist/react/useTaskSession.js +11 -22
  45. package/dist/react/useUpdateRecord.js +9 -16
  46. package/dist/react/useWorkflowResume.d.ts +18 -0
  47. package/dist/react/useWorkflowResume.js +45 -0
  48. package/dist/react/useWorkflowRun.d.ts +21 -0
  49. package/dist/react/useWorkflowRun.js +49 -0
  50. package/dist/react/useWorkflowRuns.d.ts +33 -0
  51. package/dist/react/useWorkflowRuns.js +149 -0
  52. package/dist/react/useWorkflowStart.js +20 -27
  53. package/dist/react/utils.d.ts +5 -0
  54. package/dist/react/utils.js +25 -0
  55. package/dist/types.d.ts +1 -0
  56. package/package.json +2 -4
  57. package/dist/react/components/AssistantChrome.d.ts +0 -86
  58. package/dist/react/components/AssistantChrome.js +0 -48
  59. package/dist/react/components/AssistantEmbedded.d.ts +0 -10
  60. package/dist/react/components/AssistantEmbedded.js +0 -15
  61. package/dist/react/components/AssistantExperience.d.ts +0 -96
  62. package/dist/react/components/AssistantExperience.js +0 -1294
  63. package/dist/react/components/assistant-types.d.ts +0 -80
  64. package/dist/react/components/assistant-types.js +0 -1
@@ -0,0 +1,68 @@
1
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
+ import { normalizeError } from "./utils.js";
3
+ export function useCurrentUser({ client, enabled = true, autoLoad = true, }) {
4
+ const [user, setUser] = useState(null);
5
+ const [isLoading, setIsLoading] = useState(false);
6
+ const [error, setError] = useState(null);
7
+ const refresh = useCallback(async (signal) => {
8
+ if (!enabled) {
9
+ setUser(null);
10
+ setError(null);
11
+ setIsLoading(false);
12
+ return null;
13
+ }
14
+ setIsLoading(true);
15
+ setError(null);
16
+ try {
17
+ const nextUser = await client.users.current();
18
+ if (signal?.aborted)
19
+ return null;
20
+ setUser(nextUser);
21
+ return nextUser;
22
+ }
23
+ catch (refreshError) {
24
+ if (signal?.aborted)
25
+ return null;
26
+ const normalized = normalizeError(refreshError, "Failed to load current user.");
27
+ setError(normalized);
28
+ setUser(null);
29
+ return null;
30
+ }
31
+ finally {
32
+ if (!signal?.aborted)
33
+ setIsLoading(false);
34
+ }
35
+ }, [client, enabled]);
36
+ useEffect(() => {
37
+ if (!enabled) {
38
+ setUser(null);
39
+ setError(null);
40
+ setIsLoading(false);
41
+ return;
42
+ }
43
+ if (!autoLoad)
44
+ return;
45
+ const controller = new AbortController();
46
+ let cancelled = false;
47
+ (async () => {
48
+ try {
49
+ await refresh(controller.signal);
50
+ }
51
+ catch {
52
+ if (!cancelled) {
53
+ setError(normalizeError(new Error("Failed to load current user."), "Failed to load current user."));
54
+ }
55
+ }
56
+ })();
57
+ return () => {
58
+ cancelled = true;
59
+ controller.abort();
60
+ };
61
+ }, [autoLoad, enabled, refresh]);
62
+ return useMemo(() => ({
63
+ user,
64
+ isLoading,
65
+ error,
66
+ refresh,
67
+ }), [error, isLoading, refresh, user]);
68
+ }
@@ -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
- }
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { normalizeError, resolvePodClient } from "./utils.js";
12
3
  export function useDeleteRecord({ client, podId, tableName, recordId = null, enabled = true, onSuccess, onError, }) {
13
4
  const [isSubmitting, setIsSubmitting] = useState(false);
14
5
  const [error, setError] = useState(null);
15
6
  const [lastMessage, setLastMessage] = 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,8 +16,6 @@ export function useDeleteRecord({ 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 deletion requires a table and record ID.");
25
- setError(disabledError);
26
19
  return false;
27
20
  }
28
21
  setIsSubmitting(true);
@@ -31,19 +24,19 @@ export function useDeleteRecord({ client, podId, tableName, recordId = null, ena
31
24
  const scopedClient = resolvePodClient(client, podId);
32
25
  const response = await scopedClient.records.delete(trimmedTableName, nextRecordId);
33
26
  setLastMessage(response.message ?? "Record deleted.");
34
- onSuccess?.(response);
27
+ onSuccessRef.current?.(response);
35
28
  return true;
36
29
  }
37
30
  catch (mutationError) {
38
31
  const normalized = normalizeError(mutationError, "Failed to delete record.");
39
32
  setError(normalized);
40
- onError?.(mutationError);
33
+ onErrorRef.current?.(mutationError);
41
34
  return false;
42
35
  }
43
36
  finally {
44
37
  setIsSubmitting(false);
45
38
  }
46
- }, [client, isEnabled, onError, onSuccess, podId, trimmedRecordId, trimmedTableName]);
39
+ }, [client, isEnabled, podId, trimmedRecordId, trimmedTableName]);
47
40
  const reset = useCallback(() => {
48
41
  setError(null);
49
42
  setIsSubmitting(false);
@@ -1,9 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
- function normalizeError(error, fallback) {
3
- if (error instanceof Error)
4
- return error;
5
- return new Error(fallback);
6
- }
2
+ import { normalizeError } from "./utils.js";
7
3
  export function useFlowRunHistory({ session, flowName, limit = 100, autoRefresh = true, }) {
8
4
  const { listHistory, run: liveRun, runId: liveRunId, setRunId } = session;
9
5
  const [runs, setRuns] = useState([]);
@@ -1,12 +1,6 @@
1
- import { useCallback, useEffect, useState } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { isTerminalFlowStatus, normalizeRunStatus, sleep } from "../run-utils.js";
3
- function resolvePodId(client, podId) {
4
- const resolved = podId ?? client.podId;
5
- if (!resolved) {
6
- throw new Error("podId is required. Pass podId or set it on LemmaClient.");
7
- }
8
- return resolved;
9
- }
3
+ import { normalizeError, resolvePodClient, resolvePodId } from "./utils.js";
10
4
  function resolveFlowName(base, override) {
11
5
  const resolved = override ?? base;
12
6
  if (!resolved) {
@@ -21,17 +15,16 @@ function resolveRunId(base, override) {
21
15
  }
22
16
  return resolved;
23
17
  }
24
- function normalizeError(error, fallback) {
25
- if (error instanceof Error)
26
- return error;
27
- return new Error(fallback);
28
- }
29
18
  export function useFlowSession({ client, podId, flowName, runId: initialRunId = null, autoPoll = true, pollIntervalMs = 2000, onRun, onError, }) {
30
19
  const [runId, setRunIdState] = useState(initialRunId);
31
20
  const [run, setRun] = useState(null);
32
21
  const [status, setStatus] = useState(undefined);
33
22
  const [isPolling, setIsPolling] = useState(false);
34
23
  const [error, setError] = useState(null);
24
+ const onRunRef = useRef(onRun);
25
+ const onErrorRef = useRef(onError);
26
+ useEffect(() => { onRunRef.current = onRun; }, [onRun]);
27
+ useEffect(() => { onErrorRef.current = onError; }, [onError]);
35
28
  const setRunId = useCallback((nextRunId) => {
36
29
  setRunIdState(nextRunId);
37
30
  if (!nextRunId) {
@@ -49,21 +42,21 @@ export function useFlowSession({ client, podId, flowName, runId: initialRunId =
49
42
  setRun(nextRun);
50
43
  const nextStatus = normalizeRunStatus(nextRun.status);
51
44
  setStatus(nextStatus);
52
- onRun?.(nextRun);
45
+ onRunRef.current?.(nextRun);
53
46
  return nextRun;
54
47
  }
55
48
  catch (refreshError) {
56
49
  const normalized = normalizeError(refreshError, "Failed to fetch flow run.");
57
50
  setError(normalized);
58
- onError?.(refreshError);
51
+ onErrorRef.current?.(refreshError);
59
52
  return null;
60
53
  }
61
- }, [client, onError, onRun, podId, runId]);
54
+ }, [client, podId, runId]);
62
55
  const listHistory = useCallback(async (options = {}) => {
63
56
  try {
64
- client.setPodId(resolvePodId(client, podId));
57
+ const scopedClient = resolvePodClient(client, resolvePodId(client, podId));
65
58
  const name = resolveFlowName(flowName, options.flowName);
66
- const response = await client.workflows.runs.list(name, {
59
+ const response = await scopedClient.workflows.runs.list(name, {
67
60
  limit: options.limit,
68
61
  pageToken: options.pageToken,
69
62
  });
@@ -72,25 +65,25 @@ export function useFlowSession({ client, podId, flowName, runId: initialRunId =
72
65
  catch (listError) {
73
66
  const normalized = normalizeError(listError, "Failed to list flow runs.");
74
67
  setError(normalized);
75
- onError?.(listError);
68
+ onErrorRef.current?.(listError);
76
69
  return [];
77
70
  }
78
- }, [client, flowName, onError, podId]);
71
+ }, [client, flowName, podId]);
79
72
  const start = useCallback(async (options = {}) => {
80
73
  setError(null);
81
- client.setPodId(resolvePodId(client, podId));
74
+ const scopedClient = resolvePodClient(client, resolvePodId(client, podId));
82
75
  const name = resolveFlowName(flowName, options.flowName);
83
- const created = await client.workflows.runs.start(name, options.inputs);
76
+ const created = await scopedClient.workflows.runs.start(name, options.inputs);
84
77
  setRun(created);
85
78
  setRunIdState(created.id ?? null);
86
79
  const nextStatus = normalizeRunStatus(created.status);
87
80
  setStatus(nextStatus);
88
- onRun?.(created);
81
+ onRunRef.current?.(created);
89
82
  if (options.connect !== false && created.id) {
90
83
  await refresh(created.id);
91
84
  }
92
85
  return created;
93
- }, [client, flowName, onRun, podId, refresh]);
86
+ }, [client, flowName, podId, refresh]);
94
87
  const resume = useCallback(async (options) => {
95
88
  setError(null);
96
89
  const resolvedPodId = resolvePodId(client, podId);
@@ -100,12 +93,12 @@ export function useFlowSession({ client, podId, flowName, runId: initialRunId =
100
93
  setRunIdState(resumed.id ?? id);
101
94
  const nextStatus = normalizeRunStatus(resumed.status);
102
95
  setStatus(nextStatus);
103
- onRun?.(resumed);
96
+ onRunRef.current?.(resumed);
104
97
  if (options.connect !== false) {
105
98
  await refresh(resumed.id ?? id);
106
99
  }
107
100
  return resumed;
108
- }, [client, onRun, podId, refresh, runId]);
101
+ }, [client, podId, refresh, runId]);
109
102
  const cancel = useCallback(async (explicitRunId) => {
110
103
  try {
111
104
  const resolvedPodId = resolvePodId(client, podId);
@@ -116,9 +109,9 @@ export function useFlowSession({ client, podId, flowName, runId: initialRunId =
116
109
  catch (cancelError) {
117
110
  const normalized = normalizeError(cancelError, "Failed to cancel flow run.");
118
111
  setError(normalized);
119
- onError?.(cancelError);
112
+ onErrorRef.current?.(cancelError);
120
113
  }
121
- }, [client, onError, podId, refresh, runId]);
114
+ }, [client, podId, refresh, runId]);
122
115
  const retry = useCallback(async (explicitRunId) => {
123
116
  try {
124
117
  const resolvedPodId = resolvePodId(client, podId);
@@ -129,14 +122,29 @@ export function useFlowSession({ client, podId, flowName, runId: initialRunId =
129
122
  catch (retryError) {
130
123
  const normalized = normalizeError(retryError, "Failed to retry flow run.");
131
124
  setError(normalized);
132
- onError?.(retryError);
125
+ onErrorRef.current?.(retryError);
133
126
  }
134
- }, [client, onError, podId, refresh, runId]);
127
+ }, [client, podId, refresh, runId]);
135
128
  useEffect(() => {
136
129
  if (!runId) {
137
130
  return;
138
131
  }
139
- void refresh(runId);
132
+ const controller = new AbortController();
133
+ let cancelled = false;
134
+ (async () => {
135
+ try {
136
+ await refresh(runId);
137
+ }
138
+ catch {
139
+ if (!cancelled) {
140
+ // refresh handles errors internally
141
+ }
142
+ }
143
+ })();
144
+ return () => {
145
+ cancelled = true;
146
+ controller.abort();
147
+ };
140
148
  }, [refresh, runId]);
141
149
  useEffect(() => {
142
150
  if (!autoPoll || !runId) {
@@ -170,14 +178,14 @@ export function useFlowSession({ client, podId, flowName, runId: initialRunId =
170
178
  void loop().catch((pollError) => {
171
179
  const normalized = normalizeError(pollError, "Failed while polling flow run.");
172
180
  setError(normalized);
173
- onError?.(pollError);
181
+ onErrorRef.current?.(pollError);
174
182
  setIsPolling(false);
175
183
  });
176
184
  return () => {
177
185
  active = false;
178
186
  abortController.abort();
179
187
  };
180
- }, [autoPoll, onError, pollIntervalMs, refresh, runId]);
188
+ }, [autoPoll, pollIntervalMs, refresh, runId]);
181
189
  return {
182
190
  runId,
183
191
  run,
@@ -1,18 +1,7 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { parseForeignKeyReference } from "../datastore-query.js";
3
+ import { normalizeError, resolvePodId } from "./utils.js";
3
4
  const EMPTY_LABEL_FIELDS = [];
4
- function resolvePodId(client, podId) {
5
- const resolved = podId ?? client.podId;
6
- if (!resolved) {
7
- throw new Error("podId is required. Pass podId or set it on LemmaClient.");
8
- }
9
- return resolved;
10
- }
11
- function normalizeError(error, fallback) {
12
- if (error instanceof Error)
13
- return error;
14
- return new Error(fallback);
15
- }
16
5
  function readRecordValue(record, field) {
17
6
  if (!field)
18
7
  return undefined;
@@ -67,7 +56,7 @@ export function useForeignKeyOptions({ client, podId, tableName, columnName, lab
67
56
  const [error, setError] = useState(null);
68
57
  const labelFieldsKey = useMemo(() => JSON.stringify(labelFields), [labelFields]);
69
58
  const stableLabelFields = useMemo(() => labelFields, [labelFieldsKey]);
70
- const refresh = useCallback(async () => {
59
+ const refresh = useCallback(async (signal) => {
71
60
  if (!enabled)
72
61
  return [];
73
62
  setIsLoading(true);
@@ -76,6 +65,8 @@ export function useForeignKeyOptions({ client, podId, tableName, columnName, lab
76
65
  const resolvedPodId = resolvePodId(client, podId);
77
66
  const scopedClient = resolvedPodId === client.podId ? client : client.withPod(resolvedPodId);
78
67
  const nextTable = await scopedClient.tables.get(tableName);
68
+ if (signal?.aborted)
69
+ return [];
79
70
  const nextColumn = nextTable.columns.find((entry) => entry.name === columnName) ?? null;
80
71
  const nextReference = nextColumn?.foreign_key?.references
81
72
  ? parseForeignKeyReference(nextColumn.foreign_key.references)
@@ -98,6 +89,8 @@ export function useForeignKeyOptions({ client, podId, tableName, columnName, lab
98
89
  ? [{ field: labelField, op: "ilike", value: `%${search?.trim()}%` }]
99
90
  : undefined,
100
91
  });
92
+ if (signal?.aborted)
93
+ return [];
101
94
  const records = response.items ?? [];
102
95
  const nextResolvedLabelField = pickResolvedLabelField(records, nextReference.column, labelField, stableLabelFields);
103
96
  const searchableFields = Array.from(new Set([nextResolvedLabelField, ...stableLabelFields, nextReference.column, "id"]
@@ -124,18 +117,36 @@ export function useForeignKeyOptions({ client, podId, tableName, columnName, lab
124
117
  return nextOptions;
125
118
  }
126
119
  catch (refreshError) {
120
+ if (signal?.aborted)
121
+ return [];
127
122
  const normalized = normalizeError(refreshError, "Failed to load foreign key options.");
128
123
  setError(normalized);
129
124
  return [];
130
125
  }
131
126
  finally {
132
- setIsLoading(false);
127
+ if (!signal?.aborted)
128
+ setIsLoading(false);
133
129
  }
134
130
  }, [client, columnName, enabled, labelField, limit, podId, search, stableLabelFields, tableName]);
135
131
  useEffect(() => {
136
132
  if (!enabled || !autoLoad)
137
133
  return;
138
- void refresh();
134
+ const controller = new AbortController();
135
+ let cancelled = false;
136
+ (async () => {
137
+ try {
138
+ await refresh(controller.signal);
139
+ }
140
+ catch {
141
+ if (!cancelled) {
142
+ setError(normalizeError(new Error("Failed to load foreign key options."), "Failed to load foreign key options."));
143
+ }
144
+ }
145
+ })();
146
+ return () => {
147
+ cancelled = true;
148
+ controller.abort();
149
+ };
139
150
  }, [autoLoad, enabled, refresh]);
140
151
  return useMemo(() => ({
141
152
  table,
@@ -0,0 +1,19 @@
1
+ import type { FunctionRun } from "../types.js";
2
+ import { type UseFunctionSessionOptions, type UseFunctionSessionResult } from "./useFunctionSession.js";
3
+ export interface UseFunctionRunOptions extends UseFunctionSessionOptions {
4
+ }
5
+ export interface UseFunctionRunResult extends Omit<UseFunctionSessionResult, "start" | "listHistory"> {
6
+ output: FunctionRun["output_data"];
7
+ finalOutput: FunctionRun["output_data"];
8
+ isFinished: boolean;
9
+ start: (input?: Record<string, unknown>, options?: {
10
+ functionName?: string;
11
+ connect?: boolean;
12
+ }) => Promise<FunctionRun>;
13
+ listRuns: (options?: {
14
+ functionName?: string;
15
+ limit?: number;
16
+ pageToken?: string;
17
+ }) => Promise<FunctionRun[]>;
18
+ }
19
+ export declare function useFunctionRun(options: UseFunctionRunOptions): UseFunctionRunResult;
@@ -0,0 +1,30 @@
1
+ import { useCallback, useMemo } from "react";
2
+ import { isTerminalFunctionStatus, normalizeRunStatus } from "../run-utils.js";
3
+ import { useFunctionSession, } from "./useFunctionSession.js";
4
+ export function useFunctionRun(options) {
5
+ const session = useFunctionSession(options);
6
+ const start = useCallback(async (input, startOptions = {}) => {
7
+ return session.start({
8
+ functionName: startOptions.functionName,
9
+ input,
10
+ connect: startOptions.connect,
11
+ });
12
+ }, [session]);
13
+ const listRuns = useCallback(async (listOptions = {}) => {
14
+ return session.listHistory(listOptions);
15
+ }, [session]);
16
+ return useMemo(() => {
17
+ const normalizedStatus = normalizeRunStatus(session.status);
18
+ const isFinished = isTerminalFunctionStatus(normalizedStatus);
19
+ const output = session.run?.output_data ?? null;
20
+ const finalOutput = isFinished ? output : null;
21
+ return {
22
+ ...session,
23
+ output,
24
+ finalOutput,
25
+ isFinished,
26
+ start,
27
+ listRuns,
28
+ };
29
+ }, [listRuns, session, start]);
30
+ }
@@ -0,0 +1,33 @@
1
+ import type { LemmaClient } from "../client.js";
2
+ import type { FunctionRun } from "../types.js";
3
+ export interface UseFunctionRunsOptions {
4
+ client: LemmaClient;
5
+ podId?: string;
6
+ functionName: string;
7
+ enabled?: boolean;
8
+ autoLoad?: boolean;
9
+ limit?: number;
10
+ pageToken?: string;
11
+ initialRunId?: string | null;
12
+ }
13
+ export interface UseFunctionRunsResult {
14
+ runs: FunctionRun[];
15
+ total: number;
16
+ nextPageToken: string | null;
17
+ selectedRunId: string | null;
18
+ effectiveSelectedRunId: string | null;
19
+ selectedRun: FunctionRun | 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<FunctionRun[]>;
29
+ loadMore: (overrides?: {
30
+ limit?: number;
31
+ }) => Promise<FunctionRun[]>;
32
+ }
33
+ export declare function useFunctionRuns({ client, podId, functionName, enabled, autoLoad, limit, pageToken, initialRunId, }: UseFunctionRunsOptions): UseFunctionRunsResult;
@@ -0,0 +1,149 @@
1
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
+ import { normalizeError, resolvePodClient } from "./utils.js";
3
+ export function useFunctionRuns({ client, podId, functionName, enabled = true, autoLoad = true, limit = 100, pageToken, initialRunId = null, }) {
4
+ const [runs, setRuns] = useState([]);
5
+ const [total, setTotal] = useState(0);
6
+ const [nextPageToken, setNextPageToken] = useState(null);
7
+ const [selectedRunId, setSelectedRunId] = useState(initialRunId);
8
+ const [isLoading, setIsLoading] = useState(false);
9
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
10
+ const [error, setError] = useState(null);
11
+ const trimmedFunctionName = functionName.trim();
12
+ const isEnabled = enabled && trimmedFunctionName.length > 0;
13
+ const refresh = useCallback(async (overrides = {}, signal) => {
14
+ if (!isEnabled) {
15
+ setRuns([]);
16
+ setTotal(0);
17
+ setNextPageToken(null);
18
+ setError(null);
19
+ setIsLoading(false);
20
+ return [];
21
+ }
22
+ setIsLoading(true);
23
+ setError(null);
24
+ try {
25
+ const scopedClient = resolvePodClient(client, podId);
26
+ const response = await scopedClient.functions.runs.list(trimmedFunctionName, {
27
+ limit: overrides.limit ?? limit,
28
+ pageToken: overrides.pageToken ?? pageToken,
29
+ });
30
+ if (signal?.aborted)
31
+ return [];
32
+ const nextRuns = response.items ?? [];
33
+ setRuns(nextRuns);
34
+ setTotal(response.total ?? nextRuns.length);
35
+ setNextPageToken(response.next_page_token ?? null);
36
+ setSelectedRunId((current) => (current && nextRuns.some((run) => run.id === current) ? current : initialRunId));
37
+ return nextRuns;
38
+ }
39
+ catch (refreshError) {
40
+ if (signal?.aborted)
41
+ return [];
42
+ const normalized = normalizeError(refreshError, "Failed to load function runs.");
43
+ setError(normalized);
44
+ return [];
45
+ }
46
+ finally {
47
+ if (!signal?.aborted)
48
+ setIsLoading(false);
49
+ }
50
+ }, [client, initialRunId, isEnabled, limit, pageToken, podId, trimmedFunctionName]);
51
+ const loadMore = useCallback(async (overrides = {}) => {
52
+ if (!isEnabled || !nextPageToken || isLoading || isLoadingMore) {
53
+ return [];
54
+ }
55
+ setIsLoadingMore(true);
56
+ setError(null);
57
+ try {
58
+ const scopedClient = resolvePodClient(client, podId);
59
+ const response = await scopedClient.functions.runs.list(trimmedFunctionName, {
60
+ limit: overrides.limit ?? limit,
61
+ pageToken: nextPageToken,
62
+ });
63
+ const moreRuns = response.items ?? [];
64
+ setRuns((previous) => [...previous, ...moreRuns]);
65
+ setTotal(response.total ?? runs.length + moreRuns.length);
66
+ setNextPageToken(response.next_page_token ?? null);
67
+ return moreRuns;
68
+ }
69
+ catch (loadError) {
70
+ const normalized = normalizeError(loadError, "Failed to load more function runs.");
71
+ setError(normalized);
72
+ return [];
73
+ }
74
+ finally {
75
+ setIsLoadingMore(false);
76
+ }
77
+ }, [client, isEnabled, isLoading, isLoadingMore, limit, nextPageToken, podId, trimmedFunctionName]);
78
+ useEffect(() => {
79
+ setSelectedRunId(initialRunId);
80
+ }, [initialRunId]);
81
+ useEffect(() => {
82
+ if (!isEnabled) {
83
+ setRuns([]);
84
+ setTotal(0);
85
+ setNextPageToken(null);
86
+ setError(null);
87
+ setIsLoading(false);
88
+ setIsLoadingMore(false);
89
+ return;
90
+ }
91
+ if (!autoLoad)
92
+ return;
93
+ const controller = new AbortController();
94
+ let cancelled = false;
95
+ (async () => {
96
+ try {
97
+ await refresh({}, controller.signal);
98
+ }
99
+ catch {
100
+ if (!cancelled) {
101
+ setError(normalizeError(new Error("Failed to load function runs."), "Failed to load function runs."));
102
+ }
103
+ }
104
+ })();
105
+ return () => {
106
+ cancelled = true;
107
+ controller.abort();
108
+ };
109
+ }, [autoLoad, isEnabled, refresh]);
110
+ const selectRun = useCallback((runId) => {
111
+ setSelectedRunId(runId);
112
+ }, []);
113
+ const clearSelection = useCallback(() => {
114
+ setSelectedRunId(null);
115
+ }, []);
116
+ return useMemo(() => {
117
+ const effectiveSelectedRunId = selectedRunId ?? runs[0]?.id ?? null;
118
+ const selectedRun = effectiveSelectedRunId
119
+ ? runs.find((run) => run.id === effectiveSelectedRunId) ?? null
120
+ : null;
121
+ return {
122
+ runs,
123
+ total,
124
+ nextPageToken,
125
+ selectedRunId,
126
+ effectiveSelectedRunId,
127
+ selectedRun,
128
+ isLoading,
129
+ isLoadingMore,
130
+ error,
131
+ selectRun,
132
+ clearSelection,
133
+ refresh,
134
+ loadMore,
135
+ };
136
+ }, [
137
+ clearSelection,
138
+ error,
139
+ isLoading,
140
+ isLoadingMore,
141
+ loadMore,
142
+ nextPageToken,
143
+ refresh,
144
+ runs,
145
+ selectRun,
146
+ selectedRunId,
147
+ total,
148
+ ]);
149
+ }