lemma-sdk 0.2.28 → 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.
- package/README.md +241 -201
- package/bin/lemma-sdk.js +108 -0
- package/dist/browser/lemma-client.js +125 -4
- package/dist/client.d.ts +2 -0
- package/dist/client.js +3 -0
- package/dist/config.d.ts +2 -2
- package/dist/config.js +2 -2
- package/dist/datastore-query.d.ts +54 -0
- package/dist/datastore-query.js +157 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +3 -0
- package/dist/namespaces/datastore.d.ts +9 -0
- package/dist/namespaces/datastore.js +13 -0
- package/dist/namespaces/records.d.ts +1 -1
- package/dist/openapi_client/index.d.ts +4 -0
- package/dist/openapi_client/index.js +1 -0
- package/dist/openapi_client/models/ConvertedArtifactResponse.d.ts +6 -0
- package/dist/openapi_client/models/ConvertedFileResponse.d.ts +10 -0
- package/dist/openapi_client/models/ConvertedFileResponse.js +1 -0
- package/dist/openapi_client/models/CreateFolderRequest.d.ts +3 -1
- package/dist/openapi_client/models/FlowRunEntity.d.ts +1 -0
- package/dist/openapi_client/models/ScheduledFlowStart.d.ts +3 -6
- package/dist/openapi_client/models/ScheduledFlowStartType.d.ts +4 -0
- package/dist/openapi_client/models/ScheduledFlowStartType.js +9 -0
- package/dist/openapi_client/models/WorkflowInstallRequest.d.ts +5 -0
- package/dist/openapi_client/models/WorkflowTimeInstallConfig.d.ts +19 -0
- package/dist/openapi_client/models/WorkflowTimeInstallConfig.js +1 -0
- package/dist/openapi_client/services/FilesService.d.ts +27 -1
- package/dist/openapi_client/services/FilesService.js +69 -1
- package/dist/openapi_client/services/WorkflowsService.d.ts +1 -1
- package/dist/openapi_client/services/WorkflowsService.js +1 -1
- package/dist/react/assistant-output.d.ts +6 -0
- package/dist/react/assistant-output.js +90 -0
- package/dist/react/index.d.ts +62 -8
- package/dist/react/index.js +31 -4
- package/dist/react/useAgentInputSchema.d.ts +19 -0
- package/dist/react/useAgentInputSchema.js +73 -0
- package/dist/react/useAgentRun.d.ts +17 -0
- package/dist/react/useAgentRun.js +56 -0
- package/dist/react/useAgentRuns.d.ts +33 -0
- package/dist/react/useAgentRuns.js +149 -0
- package/dist/react/useAssistantRun.d.ts +9 -0
- package/dist/react/useAssistantRun.js +28 -17
- package/dist/react/useAssistantSession.d.ts +5 -0
- package/dist/react/useAssistantSession.js +135 -86
- package/dist/react/useBulkRecords.d.ts +20 -0
- package/dist/react/useBulkRecords.js +65 -0
- package/dist/react/useConversation.d.ts +18 -0
- package/dist/react/useConversation.js +75 -0
- package/dist/react/useConversationMessages.d.ts +59 -0
- package/dist/react/useConversationMessages.js +167 -0
- package/dist/react/useConversations.d.ts +52 -0
- package/dist/react/useConversations.js +228 -0
- package/dist/react/useCreateRecord.d.ts +18 -0
- package/dist/react/useCreateRecord.js +51 -0
- package/dist/react/useCurrentUser.d.ts +14 -0
- package/dist/react/useCurrentUser.js +68 -0
- package/dist/react/useDeleteRecord.d.ts +21 -0
- package/dist/react/useDeleteRecord.js +52 -0
- package/dist/react/useFlowRunHistory.js +1 -5
- package/dist/react/useFlowSession.js +41 -33
- package/dist/react/useForeignKeyOptions.d.ts +31 -0
- package/dist/react/useForeignKeyOptions.js +161 -0
- package/dist/react/useFunctionRun.d.ts +19 -0
- package/dist/react/useFunctionRun.js +30 -0
- package/dist/react/useFunctionRuns.d.ts +33 -0
- package/dist/react/useFunctionRuns.js +149 -0
- package/dist/react/useFunctionSession.js +37 -29
- package/dist/react/useJoinedRecords.d.ts +18 -0
- package/dist/react/useJoinedRecords.js +80 -0
- package/dist/react/useMembers.d.ts +26 -0
- package/dist/react/useMembers.js +98 -0
- package/dist/react/useOrganizationMembers.d.ts +26 -0
- package/dist/react/useOrganizationMembers.js +113 -0
- package/dist/react/usePodAccess.d.ts +22 -0
- package/dist/react/usePodAccess.js +128 -0
- package/dist/react/useRecord.d.ts +18 -0
- package/dist/react/useRecord.js +75 -0
- package/dist/react/useRecordForm.d.ts +42 -0
- package/dist/react/useRecordForm.js +221 -0
- package/dist/react/useRecordSchema.d.ts +20 -0
- package/dist/react/useRecordSchema.js +24 -0
- package/dist/react/useRecords.d.ts +20 -0
- package/dist/react/useRecords.js +146 -0
- package/dist/react/useRelatedRecords.d.ts +43 -0
- package/dist/react/useRelatedRecords.js +239 -0
- package/dist/react/useReverseRelatedRecords.d.ts +47 -0
- package/dist/react/useReverseRelatedRecords.js +235 -0
- package/dist/react/useSchemaForm.d.ts +24 -0
- package/dist/react/useSchemaForm.js +104 -0
- package/dist/react/useTable.d.ts +16 -0
- package/dist/react/useTable.js +70 -0
- package/dist/react/useTables.d.ts +26 -0
- package/dist/react/useTables.js +113 -0
- package/dist/react/useTaskSession.js +11 -22
- package/dist/react/useUpdateRecord.d.ts +21 -0
- package/dist/react/useUpdateRecord.js +55 -0
- package/dist/react/useWorkflowResume.d.ts +18 -0
- package/dist/react/useWorkflowResume.js +45 -0
- package/dist/react/useWorkflowRun.d.ts +21 -0
- package/dist/react/useWorkflowRun.js +49 -0
- package/dist/react/useWorkflowRuns.d.ts +33 -0
- package/dist/react/useWorkflowRuns.js +149 -0
- package/dist/react/useWorkflowStart.d.ts +33 -0
- package/dist/react/useWorkflowStart.js +148 -0
- package/dist/react/utils.d.ts +5 -0
- package/dist/react/utils.js +25 -0
- package/dist/record-form.d.ts +30 -0
- package/dist/record-form.js +199 -0
- package/dist/schema-form.d.ts +41 -0
- package/dist/schema-form.js +200 -0
- package/dist/types.d.ts +6 -1
- package/package.json +11 -8
- package/dist/react/components/AssistantChrome.d.ts +0 -86
- package/dist/react/components/AssistantChrome.js +0 -48
- package/dist/react/components/AssistantEmbedded.d.ts +0 -10
- package/dist/react/components/AssistantEmbedded.js +0 -15
- package/dist/react/components/AssistantExperience.d.ts +0 -96
- package/dist/react/components/AssistantExperience.js +0 -1294
- package/dist/react/components/assistant-types.d.ts +0 -80
- package/dist/react/styles.css +0 -2407
- /package/dist/{react/components/assistant-types.js → openapi_client/models/ConvertedArtifactResponse.js} +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
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, onSuccess, onError, }) {
|
|
4
|
+
const [updatedRecord, setUpdatedRecord] = useState(null);
|
|
5
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
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]);
|
|
11
|
+
const trimmedTableName = tableName.trim();
|
|
12
|
+
const trimmedRecordId = typeof recordId === "string" ? recordId.trim() : "";
|
|
13
|
+
const isEnabled = enabled && trimmedTableName.length > 0;
|
|
14
|
+
const update = useCallback(async (data, overrides = {}) => {
|
|
15
|
+
const nextRecordId = typeof overrides.recordId === "string"
|
|
16
|
+
? overrides.recordId.trim()
|
|
17
|
+
: trimmedRecordId;
|
|
18
|
+
if (!isEnabled || nextRecordId.length === 0) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
setIsSubmitting(true);
|
|
22
|
+
setError(null);
|
|
23
|
+
try {
|
|
24
|
+
const scopedClient = resolvePodClient(client, podId);
|
|
25
|
+
const response = await scopedClient.records.update(trimmedTableName, nextRecordId, data);
|
|
26
|
+
const nextRecord = (response.data ?? null);
|
|
27
|
+
setUpdatedRecord(nextRecord);
|
|
28
|
+
if (nextRecord) {
|
|
29
|
+
onSuccessRef.current?.(nextRecord, response);
|
|
30
|
+
}
|
|
31
|
+
return nextRecord;
|
|
32
|
+
}
|
|
33
|
+
catch (mutationError) {
|
|
34
|
+
const normalized = normalizeError(mutationError, "Failed to update record.");
|
|
35
|
+
setError(normalized);
|
|
36
|
+
onErrorRef.current?.(mutationError);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
setIsSubmitting(false);
|
|
41
|
+
}
|
|
42
|
+
}, [client, isEnabled, podId, trimmedRecordId, trimmedTableName]);
|
|
43
|
+
const reset = useCallback(() => {
|
|
44
|
+
setUpdatedRecord(null);
|
|
45
|
+
setError(null);
|
|
46
|
+
setIsSubmitting(false);
|
|
47
|
+
}, []);
|
|
48
|
+
return useMemo(() => ({
|
|
49
|
+
updatedRecord,
|
|
50
|
+
isSubmitting,
|
|
51
|
+
error,
|
|
52
|
+
update,
|
|
53
|
+
reset,
|
|
54
|
+
}), [error, isSubmitting, reset, update, updatedRecord]);
|
|
55
|
+
}
|
|
@@ -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;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { normalizeError, resolvePodClient } from "./utils.js";
|
|
3
|
+
export function useWorkflowRuns({ client, podId, workflowName, 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 trimmedWorkflowName = workflowName.trim();
|
|
12
|
+
const isEnabled = enabled && trimmedWorkflowName.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.workflows.runs.list(trimmedWorkflowName, {
|
|
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 workflow runs.");
|
|
43
|
+
setError(normalized);
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
if (!signal?.aborted)
|
|
48
|
+
setIsLoading(false);
|
|
49
|
+
}
|
|
50
|
+
}, [client, initialRunId, isEnabled, limit, pageToken, podId, trimmedWorkflowName]);
|
|
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.workflows.runs.list(trimmedWorkflowName, {
|
|
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 workflow runs.");
|
|
71
|
+
setError(normalized);
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
setIsLoadingMore(false);
|
|
76
|
+
}
|
|
77
|
+
}, [client, isEnabled, isLoading, isLoadingMore, limit, nextPageToken, podId, trimmedWorkflowName]);
|
|
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 workflow runs."), "Failed to load workflow 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
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
import type { FlowRun, FlowStartType, Workflow, WorkflowRunInputs } from "../types.js";
|
|
3
|
+
import { type UseFlowSessionResult } from "./useFlowSession.js";
|
|
4
|
+
export interface UseWorkflowStartOptions {
|
|
5
|
+
client: LemmaClient;
|
|
6
|
+
podId?: string;
|
|
7
|
+
workflowName: string;
|
|
8
|
+
runId?: string | null;
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
autoLoad?: boolean;
|
|
11
|
+
autoPoll?: boolean;
|
|
12
|
+
pollIntervalMs?: number;
|
|
13
|
+
onRun?: (run: FlowRun) => void;
|
|
14
|
+
onError?: (error: unknown) => void;
|
|
15
|
+
}
|
|
16
|
+
export interface UseWorkflowStartResult extends Omit<UseFlowSessionResult, "start" | "listHistory"> {
|
|
17
|
+
workflow: Workflow | null;
|
|
18
|
+
startType: FlowStartType | "MANUAL";
|
|
19
|
+
inputSchema: Record<string, unknown> | null;
|
|
20
|
+
inputUiSchema: Record<string, unknown> | null;
|
|
21
|
+
isLoadingWorkflow: boolean;
|
|
22
|
+
isStarting: boolean;
|
|
23
|
+
error: Error | null;
|
|
24
|
+
refreshWorkflow: () => Promise<Workflow | null>;
|
|
25
|
+
listHistory: (options?: {
|
|
26
|
+
limit?: number;
|
|
27
|
+
pageToken?: string;
|
|
28
|
+
}) => Promise<FlowRun[]>;
|
|
29
|
+
start: (inputs?: WorkflowRunInputs, options?: {
|
|
30
|
+
forceResume?: boolean;
|
|
31
|
+
}) => Promise<FlowRun>;
|
|
32
|
+
}
|
|
33
|
+
export declare function useWorkflowStart({ client, podId, workflowName, runId, enabled, autoLoad, autoPoll, pollIntervalMs, onRun, onError, }: UseWorkflowStartOptions): UseWorkflowStartResult;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { normalizeRunStatus } from "../run-utils.js";
|
|
3
|
+
import { normalizeError, resolvePodClient } from "./utils.js";
|
|
4
|
+
import { useFlowSession, } from "./useFlowSession.js";
|
|
5
|
+
function findFirstFormNode(workflow) {
|
|
6
|
+
if (!workflow?.nodes?.length)
|
|
7
|
+
return null;
|
|
8
|
+
for (const node of workflow.nodes) {
|
|
9
|
+
if (!node || typeof node !== "object")
|
|
10
|
+
continue;
|
|
11
|
+
if (!("config" in node) || typeof node.config !== "object" || !node.config)
|
|
12
|
+
continue;
|
|
13
|
+
if ("input_schema" in node.config) {
|
|
14
|
+
return node;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
function isWaitingFlowRun(run) {
|
|
20
|
+
if (!run)
|
|
21
|
+
return false;
|
|
22
|
+
const normalizedStatus = normalizeRunStatus(run.status);
|
|
23
|
+
return normalizedStatus === "WAITING"
|
|
24
|
+
|| !!run.waiting_function_run_id
|
|
25
|
+
|| !!run.waiting_task_id
|
|
26
|
+
|| !!run.waiting_trigger_id;
|
|
27
|
+
}
|
|
28
|
+
export function useWorkflowStart({ client, podId, workflowName, runId = null, enabled = true, autoLoad = true, autoPoll = true, pollIntervalMs = 2000, onRun, onError, }) {
|
|
29
|
+
const [workflow, setWorkflow] = useState(null);
|
|
30
|
+
const [workflowError, setWorkflowError] = useState(null);
|
|
31
|
+
const [isLoadingWorkflow, setIsLoadingWorkflow] = useState(false);
|
|
32
|
+
const [isStarting, setIsStarting] = useState(false);
|
|
33
|
+
const hasWorkflowName = workflowName.trim().length > 0;
|
|
34
|
+
const isEnabled = enabled && hasWorkflowName;
|
|
35
|
+
const session = useFlowSession({
|
|
36
|
+
client,
|
|
37
|
+
podId,
|
|
38
|
+
flowName: workflowName,
|
|
39
|
+
runId,
|
|
40
|
+
autoPoll,
|
|
41
|
+
pollIntervalMs,
|
|
42
|
+
onRun,
|
|
43
|
+
onError,
|
|
44
|
+
});
|
|
45
|
+
const refreshWorkflow = useCallback(async () => {
|
|
46
|
+
if (!isEnabled) {
|
|
47
|
+
setWorkflow(null);
|
|
48
|
+
setWorkflowError(null);
|
|
49
|
+
setIsLoadingWorkflow(false);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
setIsLoadingWorkflow(true);
|
|
53
|
+
setWorkflowError(null);
|
|
54
|
+
try {
|
|
55
|
+
const scopedClient = resolvePodClient(client, podId);
|
|
56
|
+
const nextWorkflow = await scopedClient.workflows.get(workflowName);
|
|
57
|
+
setWorkflow(nextWorkflow);
|
|
58
|
+
return nextWorkflow;
|
|
59
|
+
}
|
|
60
|
+
catch (refreshError) {
|
|
61
|
+
const normalized = normalizeError(refreshError, "Failed to fetch workflow definition.");
|
|
62
|
+
setWorkflowError(normalized);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
setIsLoadingWorkflow(false);
|
|
67
|
+
}
|
|
68
|
+
}, [client, isEnabled, podId, workflowName]);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!isEnabled) {
|
|
71
|
+
setWorkflow(null);
|
|
72
|
+
setWorkflowError(null);
|
|
73
|
+
setIsLoadingWorkflow(false);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (!autoLoad)
|
|
77
|
+
return;
|
|
78
|
+
void refreshWorkflow();
|
|
79
|
+
}, [autoLoad, isEnabled, refreshWorkflow]);
|
|
80
|
+
const listHistory = useCallback((options = {}) => {
|
|
81
|
+
return session.listHistory({
|
|
82
|
+
flowName: workflowName,
|
|
83
|
+
limit: options.limit,
|
|
84
|
+
pageToken: options.pageToken,
|
|
85
|
+
});
|
|
86
|
+
}, [session, workflowName]);
|
|
87
|
+
const start = useCallback(async (inputs = {}, options = {}) => {
|
|
88
|
+
if (!hasWorkflowName) {
|
|
89
|
+
const missingWorkflowError = new Error("workflowName is required.");
|
|
90
|
+
setWorkflowError(missingWorkflowError);
|
|
91
|
+
throw missingWorkflowError;
|
|
92
|
+
}
|
|
93
|
+
setIsStarting(true);
|
|
94
|
+
setWorkflowError(null);
|
|
95
|
+
try {
|
|
96
|
+
const currentWorkflow = workflow?.name === workflowName
|
|
97
|
+
? workflow
|
|
98
|
+
: await refreshWorkflow();
|
|
99
|
+
const startType = currentWorkflow?.start?.type ?? "MANUAL";
|
|
100
|
+
if (startType === "MANUAL") {
|
|
101
|
+
const created = await session.start({
|
|
102
|
+
flowName: workflowName,
|
|
103
|
+
inputs: {},
|
|
104
|
+
});
|
|
105
|
+
const shouldResume = !!created.id && (options.forceResume === true
|
|
106
|
+
|| Object.keys(inputs).length > 0
|
|
107
|
+
|| isWaitingFlowRun(created));
|
|
108
|
+
if (!shouldResume || !created.id) {
|
|
109
|
+
return created;
|
|
110
|
+
}
|
|
111
|
+
return session.resume({
|
|
112
|
+
runId: created.id,
|
|
113
|
+
inputs,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return session.start({
|
|
117
|
+
flowName: workflowName,
|
|
118
|
+
inputs,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (startError) {
|
|
122
|
+
const normalized = normalizeError(startError, "Failed to start workflow.");
|
|
123
|
+
setWorkflowError(normalized);
|
|
124
|
+
throw normalized;
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
setIsStarting(false);
|
|
128
|
+
}
|
|
129
|
+
}, [hasWorkflowName, refreshWorkflow, session, workflow, workflowName]);
|
|
130
|
+
return useMemo(() => {
|
|
131
|
+
const formNode = findFirstFormNode(workflow);
|
|
132
|
+
const startType = workflow?.start?.type ?? "MANUAL";
|
|
133
|
+
const error = workflowError ?? session.error;
|
|
134
|
+
return {
|
|
135
|
+
...session,
|
|
136
|
+
workflow,
|
|
137
|
+
startType,
|
|
138
|
+
inputSchema: formNode?.config.input_schema ?? null,
|
|
139
|
+
inputUiSchema: formNode?.config.ui_schema ?? null,
|
|
140
|
+
isLoadingWorkflow,
|
|
141
|
+
isStarting,
|
|
142
|
+
error,
|
|
143
|
+
refreshWorkflow,
|
|
144
|
+
listHistory,
|
|
145
|
+
start,
|
|
146
|
+
};
|
|
147
|
+
}, [isStarting, isLoadingWorkflow, listHistory, refreshWorkflow, session, start, workflow, workflowError]);
|
|
148
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
export declare function normalizeError(error: unknown, fallback: string): Error;
|
|
3
|
+
export declare function resolvePodClient(client: LemmaClient, podId?: string): LemmaClient;
|
|
4
|
+
export declare function resolvePodId(client: LemmaClient, podId?: string): string;
|
|
5
|
+
export declare function stringifyComparable(value: unknown): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function normalizeError(error, fallback) {
|
|
2
|
+
if (error instanceof Error)
|
|
3
|
+
return error;
|
|
4
|
+
return new Error(fallback);
|
|
5
|
+
}
|
|
6
|
+
export function resolvePodClient(client, podId) {
|
|
7
|
+
if (!podId || podId === client.podId)
|
|
8
|
+
return client;
|
|
9
|
+
return client.withPod(podId);
|
|
10
|
+
}
|
|
11
|
+
export function resolvePodId(client, podId) {
|
|
12
|
+
const resolved = podId ?? client.podId;
|
|
13
|
+
if (!resolved) {
|
|
14
|
+
throw new Error("podId is required. Pass podId or set it on LemmaClient.");
|
|
15
|
+
}
|
|
16
|
+
return resolved;
|
|
17
|
+
}
|
|
18
|
+
export function stringifyComparable(value) {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.stringify(value);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return String(value);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ForeignKeyReference } from "./datastore-query.js";
|
|
2
|
+
import { type ColumnSchema, type Table } from "./types.js";
|
|
3
|
+
export type RecordSchemaFieldKind = "text" | "textarea" | "number" | "boolean" | "json" | "date" | "datetime" | "select" | "foreign-key" | "uuid";
|
|
4
|
+
export interface RecordSchemaField {
|
|
5
|
+
name: string;
|
|
6
|
+
label: string;
|
|
7
|
+
kind: RecordSchemaFieldKind;
|
|
8
|
+
column: ColumnSchema;
|
|
9
|
+
required: boolean;
|
|
10
|
+
readOnly: boolean;
|
|
11
|
+
system: boolean;
|
|
12
|
+
computed: boolean;
|
|
13
|
+
auto: boolean;
|
|
14
|
+
options: string[];
|
|
15
|
+
foreignKey: ForeignKeyReference | null;
|
|
16
|
+
}
|
|
17
|
+
export interface BuildRecordPayloadOptions {
|
|
18
|
+
mode?: "create" | "update";
|
|
19
|
+
}
|
|
20
|
+
export interface BuildRecordPayloadResult {
|
|
21
|
+
data: Record<string, unknown>;
|
|
22
|
+
errors: Record<string, string>;
|
|
23
|
+
isValid: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function getRecordFieldKind(column: ColumnSchema): RecordSchemaFieldKind;
|
|
26
|
+
export declare function buildRecordSchemaFields(table: Table): RecordSchemaField[];
|
|
27
|
+
export declare function getEditableRecordFields(table: Table): RecordSchemaField[];
|
|
28
|
+
export declare function formatRecordValueForForm(column: ColumnSchema, value: unknown): unknown;
|
|
29
|
+
export declare function buildRecordFormValues(table: Table, values?: Record<string, unknown>): Record<string, unknown>;
|
|
30
|
+
export declare function buildRecordPayload(table: Table, values: Record<string, unknown>, options?: BuildRecordPayloadOptions): BuildRecordPayloadResult;
|