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,235 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { parseForeignKeyReference } from "../datastore-query.js";
|
|
3
|
+
import { normalizeError, resolvePodClient, stringifyComparable } from "./utils.js";
|
|
4
|
+
function sentenceCase(value) {
|
|
5
|
+
return value
|
|
6
|
+
.replace(/[_\.]/g, " ")
|
|
7
|
+
.replace(/\s+/g, " ")
|
|
8
|
+
.trim()
|
|
9
|
+
.replace(/\b\w/g, (match) => match.toUpperCase());
|
|
10
|
+
}
|
|
11
|
+
function pickDefaultFields(table, foreignKey) {
|
|
12
|
+
const names = table.columns
|
|
13
|
+
.map((column) => column.name)
|
|
14
|
+
.filter((name) => name !== "created_at" && name !== "updated_at");
|
|
15
|
+
const prioritized = ["id", "name", "title", "label", "status", foreignKey];
|
|
16
|
+
const next = [];
|
|
17
|
+
prioritized.forEach((name) => {
|
|
18
|
+
if (names.includes(name) && !next.includes(name)) {
|
|
19
|
+
next.push(name);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
names.forEach((name) => {
|
|
23
|
+
if (!next.includes(name)) {
|
|
24
|
+
next.push(name);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return next.slice(0, 6);
|
|
28
|
+
}
|
|
29
|
+
export function useReverseRelatedRecords({ client, podId, tableName, recordId = null, relation = null, fields, limit = 20, offset, sortBy, order, tablesLimit = 100, enabled = true, autoLoad = true, }) {
|
|
30
|
+
const [parentTable, setParentTable] = useState(null);
|
|
31
|
+
const [relatedTable, setRelatedTable] = useState(null);
|
|
32
|
+
const [parentRecord, setParentRecord] = useState(null);
|
|
33
|
+
const [relations, setRelations] = useState([]);
|
|
34
|
+
const [selectedRelation, setSelectedRelation] = useState(null);
|
|
35
|
+
const [columns, setColumns] = useState([]);
|
|
36
|
+
const [records, setRecords] = useState([]);
|
|
37
|
+
const [total, setTotal] = useState(0);
|
|
38
|
+
const [nextPageToken, setNextPageToken] = useState(null);
|
|
39
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
40
|
+
const [error, setError] = useState(null);
|
|
41
|
+
const trimmedTableName = tableName.trim();
|
|
42
|
+
const trimmedRecordId = typeof recordId === "string" ? recordId.trim() : "";
|
|
43
|
+
const relationKey = stringifyComparable(relation);
|
|
44
|
+
const fieldsKey = stringifyComparable(fields);
|
|
45
|
+
const stableRelation = useMemo(() => relation, [relationKey]);
|
|
46
|
+
const stableFields = useMemo(() => fields, [fieldsKey]);
|
|
47
|
+
const isEnabled = enabled && trimmedTableName.length > 0 && trimmedRecordId.length > 0;
|
|
48
|
+
const refresh = useCallback(async (signal) => {
|
|
49
|
+
if (!isEnabled) {
|
|
50
|
+
setParentTable(null);
|
|
51
|
+
setRelatedTable(null);
|
|
52
|
+
setParentRecord(null);
|
|
53
|
+
setRelations([]);
|
|
54
|
+
setSelectedRelation(null);
|
|
55
|
+
setColumns([]);
|
|
56
|
+
setRecords([]);
|
|
57
|
+
setTotal(0);
|
|
58
|
+
setNextPageToken(null);
|
|
59
|
+
setError(null);
|
|
60
|
+
setIsLoading(false);
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
setIsLoading(true);
|
|
64
|
+
setError(null);
|
|
65
|
+
try {
|
|
66
|
+
const scopedClient = resolvePodClient(client, podId);
|
|
67
|
+
const [tablesResponse, parentRecordResponse] = await Promise.all([
|
|
68
|
+
scopedClient.tables.list({ limit: tablesLimit }),
|
|
69
|
+
scopedClient.records.get(trimmedTableName, trimmedRecordId),
|
|
70
|
+
]);
|
|
71
|
+
if (signal?.aborted)
|
|
72
|
+
return [];
|
|
73
|
+
const listedTables = tablesResponse.items ?? [];
|
|
74
|
+
const nextParentTable = listedTables.find((tableEntry) => tableEntry.name === trimmedTableName)
|
|
75
|
+
?? await scopedClient.tables.get(trimmedTableName);
|
|
76
|
+
const nextParentRecord = parentRecordResponse.data ?? null;
|
|
77
|
+
if (signal?.aborted)
|
|
78
|
+
return [];
|
|
79
|
+
setParentTable(nextParentTable);
|
|
80
|
+
setParentRecord(nextParentRecord);
|
|
81
|
+
const nextRelations = listedTables.flatMap((candidateTable) => candidateTable.columns.flatMap((column) => {
|
|
82
|
+
const reference = column.foreign_key?.references
|
|
83
|
+
? parseForeignKeyReference(column.foreign_key.references)
|
|
84
|
+
: null;
|
|
85
|
+
if (!reference || reference.table !== trimmedTableName) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
return [{
|
|
89
|
+
tableName: candidateTable.name,
|
|
90
|
+
foreignKey: column.name,
|
|
91
|
+
referencedColumn: reference.column,
|
|
92
|
+
label: `${candidateTable.name}.${column.name} -> ${trimmedTableName}.${reference.column}`,
|
|
93
|
+
}];
|
|
94
|
+
}));
|
|
95
|
+
setRelations(nextRelations);
|
|
96
|
+
const nextSelectedRelation = stableRelation
|
|
97
|
+
? nextRelations.find((entry) => (entry.tableName === stableRelation.tableName
|
|
98
|
+
&& entry.foreignKey === stableRelation.foreignKey)) ?? null
|
|
99
|
+
: (nextRelations[0] ?? null);
|
|
100
|
+
setSelectedRelation(nextSelectedRelation);
|
|
101
|
+
if (!nextSelectedRelation) {
|
|
102
|
+
setRelatedTable(null);
|
|
103
|
+
setColumns([]);
|
|
104
|
+
setRecords([]);
|
|
105
|
+
setTotal(0);
|
|
106
|
+
setNextPageToken(null);
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
const nextRelatedTable = listedTables.find((tableEntry) => tableEntry.name === nextSelectedRelation.tableName)
|
|
110
|
+
?? await scopedClient.tables.get(nextSelectedRelation.tableName);
|
|
111
|
+
const referenceValue = nextParentRecord?.[nextSelectedRelation.referencedColumn]
|
|
112
|
+
?? (nextSelectedRelation.referencedColumn === "id" ? trimmedRecordId : undefined);
|
|
113
|
+
if (signal?.aborted)
|
|
114
|
+
return [];
|
|
115
|
+
setRelatedTable(nextRelatedTable);
|
|
116
|
+
if (typeof referenceValue === "undefined" || referenceValue === null) {
|
|
117
|
+
setColumns([]);
|
|
118
|
+
setRecords([]);
|
|
119
|
+
setTotal(0);
|
|
120
|
+
setNextPageToken(null);
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
const resolvedFields = (stableFields?.length ? stableFields : pickDefaultFields(nextRelatedTable, nextSelectedRelation.foreignKey))
|
|
124
|
+
.filter((field, index, allFields) => field.trim().length > 0 && allFields.indexOf(field) === index);
|
|
125
|
+
const response = await scopedClient.records.list(nextSelectedRelation.tableName, {
|
|
126
|
+
filters: [{
|
|
127
|
+
field: nextSelectedRelation.foreignKey,
|
|
128
|
+
op: "eq",
|
|
129
|
+
value: referenceValue,
|
|
130
|
+
}],
|
|
131
|
+
limit,
|
|
132
|
+
offset,
|
|
133
|
+
sortBy,
|
|
134
|
+
order,
|
|
135
|
+
});
|
|
136
|
+
if (signal?.aborted)
|
|
137
|
+
return [];
|
|
138
|
+
const nextRecords = (response.items ?? []);
|
|
139
|
+
setColumns(resolvedFields.map((field) => ({
|
|
140
|
+
key: field,
|
|
141
|
+
field,
|
|
142
|
+
label: sentenceCase(field),
|
|
143
|
+
})));
|
|
144
|
+
setRecords(nextRecords);
|
|
145
|
+
setTotal(response.total ?? nextRecords.length);
|
|
146
|
+
setNextPageToken(response.next_page_token ?? null);
|
|
147
|
+
return nextRecords;
|
|
148
|
+
}
|
|
149
|
+
catch (refreshError) {
|
|
150
|
+
if (signal?.aborted)
|
|
151
|
+
return [];
|
|
152
|
+
const normalized = normalizeError(refreshError, "Failed to load reverse-related records.");
|
|
153
|
+
setError(normalized);
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
if (!signal?.aborted)
|
|
158
|
+
setIsLoading(false);
|
|
159
|
+
}
|
|
160
|
+
}, [
|
|
161
|
+
client,
|
|
162
|
+
isEnabled,
|
|
163
|
+
limit,
|
|
164
|
+
offset,
|
|
165
|
+
order,
|
|
166
|
+
podId,
|
|
167
|
+
sortBy,
|
|
168
|
+
stableFields,
|
|
169
|
+
stableRelation,
|
|
170
|
+
tablesLimit,
|
|
171
|
+
trimmedRecordId,
|
|
172
|
+
trimmedTableName,
|
|
173
|
+
]);
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
if (!isEnabled) {
|
|
176
|
+
setParentTable(null);
|
|
177
|
+
setRelatedTable(null);
|
|
178
|
+
setParentRecord(null);
|
|
179
|
+
setRelations([]);
|
|
180
|
+
setSelectedRelation(null);
|
|
181
|
+
setColumns([]);
|
|
182
|
+
setRecords([]);
|
|
183
|
+
setTotal(0);
|
|
184
|
+
setNextPageToken(null);
|
|
185
|
+
setError(null);
|
|
186
|
+
setIsLoading(false);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (!autoLoad)
|
|
190
|
+
return;
|
|
191
|
+
const controller = new AbortController();
|
|
192
|
+
let cancelled = false;
|
|
193
|
+
(async () => {
|
|
194
|
+
try {
|
|
195
|
+
await refresh(controller.signal);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
if (!cancelled) {
|
|
199
|
+
setError(normalizeError(new Error("Failed to load reverse-related records."), "Failed to load reverse-related records."));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
})();
|
|
203
|
+
return () => {
|
|
204
|
+
cancelled = true;
|
|
205
|
+
controller.abort();
|
|
206
|
+
};
|
|
207
|
+
}, [autoLoad, isEnabled, refresh]);
|
|
208
|
+
return useMemo(() => ({
|
|
209
|
+
parentTable,
|
|
210
|
+
relatedTable,
|
|
211
|
+
parentRecord,
|
|
212
|
+
relations,
|
|
213
|
+
selectedRelation,
|
|
214
|
+
columns,
|
|
215
|
+
records,
|
|
216
|
+
total,
|
|
217
|
+
nextPageToken,
|
|
218
|
+
isLoading,
|
|
219
|
+
error,
|
|
220
|
+
refresh,
|
|
221
|
+
}), [
|
|
222
|
+
columns,
|
|
223
|
+
error,
|
|
224
|
+
isLoading,
|
|
225
|
+
nextPageToken,
|
|
226
|
+
parentRecord,
|
|
227
|
+
parentTable,
|
|
228
|
+
records,
|
|
229
|
+
refresh,
|
|
230
|
+
relatedTable,
|
|
231
|
+
relations,
|
|
232
|
+
selectedRelation,
|
|
233
|
+
total,
|
|
234
|
+
]);
|
|
235
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { JsonSchemaLike, SchemaFormField } from "../schema-form.js";
|
|
2
|
+
export interface UseSchemaFormOptions {
|
|
3
|
+
schema?: JsonSchemaLike | null;
|
|
4
|
+
uiSchema?: Record<string, unknown> | null;
|
|
5
|
+
initialValues?: Record<string, unknown>;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
onSubmit?: (data: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
8
|
+
onError?: (error: unknown) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface UseSchemaFormResult {
|
|
11
|
+
fields: SchemaFormField[];
|
|
12
|
+
values: Record<string, unknown>;
|
|
13
|
+
baselineValues: Record<string, unknown>;
|
|
14
|
+
fieldErrors: Record<string, string>;
|
|
15
|
+
isSubmitting: boolean;
|
|
16
|
+
isDirty: boolean;
|
|
17
|
+
error: Error | null;
|
|
18
|
+
setValue: (fieldName: string, value: unknown) => void;
|
|
19
|
+
setValues: (values: Record<string, unknown>) => void;
|
|
20
|
+
reset: (nextValues?: Record<string, unknown>) => void;
|
|
21
|
+
validate: () => boolean;
|
|
22
|
+
submit: () => Promise<Record<string, unknown> | null>;
|
|
23
|
+
}
|
|
24
|
+
export declare function useSchemaForm({ schema, uiSchema, initialValues, enabled, onSubmit, onError, }: UseSchemaFormOptions): UseSchemaFormResult;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { buildSchemaFormFields, buildSchemaFormPayload, buildSchemaFormValues, } from "../schema-form.js";
|
|
3
|
+
import { normalizeError, stringifyComparable } from "./utils.js";
|
|
4
|
+
const EMPTY_VALUES = {};
|
|
5
|
+
export function useSchemaForm({ schema = null, uiSchema = null, initialValues = EMPTY_VALUES, enabled = true, onSubmit, onError, }) {
|
|
6
|
+
const [values, setValuesState] = useState({});
|
|
7
|
+
const [baselineValues, setBaselineValues] = useState({});
|
|
8
|
+
const [fieldErrors, setFieldErrors] = useState({});
|
|
9
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
10
|
+
const [error, setError] = useState(null);
|
|
11
|
+
const schemaKey = stringifyComparable(schema);
|
|
12
|
+
const uiSchemaKey = stringifyComparable(uiSchema);
|
|
13
|
+
const initialValuesKey = stringifyComparable(initialValues);
|
|
14
|
+
const stableSchema = useMemo(() => schema, [schemaKey]);
|
|
15
|
+
const stableUiSchema = useMemo(() => uiSchema, [uiSchemaKey]);
|
|
16
|
+
const stableInitialValues = useMemo(() => initialValues, [initialValuesKey]);
|
|
17
|
+
const fields = useMemo(() => buildSchemaFormFields(stableSchema ?? undefined, stableUiSchema ?? undefined), [stableSchema, stableUiSchema]);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!enabled) {
|
|
20
|
+
setValuesState({});
|
|
21
|
+
setBaselineValues({});
|
|
22
|
+
setFieldErrors({});
|
|
23
|
+
setError(null);
|
|
24
|
+
setIsSubmitting(false);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const nextValues = buildSchemaFormValues(stableSchema ?? undefined, stableInitialValues, stableUiSchema ?? undefined);
|
|
28
|
+
setValuesState(nextValues);
|
|
29
|
+
setBaselineValues(nextValues);
|
|
30
|
+
setFieldErrors({});
|
|
31
|
+
}, [enabled, stableInitialValues, stableSchema, stableUiSchema]);
|
|
32
|
+
const setValue = useCallback((fieldName, value) => {
|
|
33
|
+
setValuesState((current) => ({
|
|
34
|
+
...current,
|
|
35
|
+
[fieldName]: value,
|
|
36
|
+
}));
|
|
37
|
+
setFieldErrors((current) => {
|
|
38
|
+
if (!(fieldName in current))
|
|
39
|
+
return current;
|
|
40
|
+
const next = { ...current };
|
|
41
|
+
delete next[fieldName];
|
|
42
|
+
return next;
|
|
43
|
+
});
|
|
44
|
+
}, []);
|
|
45
|
+
const setValues = useCallback((nextValues) => {
|
|
46
|
+
setValuesState((current) => ({
|
|
47
|
+
...current,
|
|
48
|
+
...nextValues,
|
|
49
|
+
}));
|
|
50
|
+
}, []);
|
|
51
|
+
const reset = useCallback((nextValues) => {
|
|
52
|
+
const resolved = buildSchemaFormValues(stableSchema ?? undefined, nextValues ?? stableInitialValues, stableUiSchema ?? undefined);
|
|
53
|
+
setValuesState(resolved);
|
|
54
|
+
setBaselineValues(resolved);
|
|
55
|
+
setFieldErrors({});
|
|
56
|
+
setError(null);
|
|
57
|
+
}, [stableInitialValues, stableSchema, stableUiSchema]);
|
|
58
|
+
const validate = useCallback(() => {
|
|
59
|
+
const result = buildSchemaFormPayload(stableSchema ?? undefined, values, stableUiSchema ?? undefined);
|
|
60
|
+
setFieldErrors(result.errors);
|
|
61
|
+
return result.isValid;
|
|
62
|
+
}, [stableSchema, stableUiSchema, values]);
|
|
63
|
+
const submit = useCallback(async () => {
|
|
64
|
+
const result = buildSchemaFormPayload(stableSchema ?? undefined, values, stableUiSchema ?? undefined);
|
|
65
|
+
setFieldErrors(result.errors);
|
|
66
|
+
if (!result.isValid) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
if (!onSubmit) {
|
|
70
|
+
return result.data;
|
|
71
|
+
}
|
|
72
|
+
setIsSubmitting(true);
|
|
73
|
+
setError(null);
|
|
74
|
+
try {
|
|
75
|
+
await onSubmit(result.data);
|
|
76
|
+
setBaselineValues(values);
|
|
77
|
+
return result.data;
|
|
78
|
+
}
|
|
79
|
+
catch (submitError) {
|
|
80
|
+
const normalized = normalizeError(submitError, "Failed to submit schema form.");
|
|
81
|
+
setError(normalized);
|
|
82
|
+
onError?.(submitError);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
setIsSubmitting(false);
|
|
87
|
+
}
|
|
88
|
+
}, [onError, onSubmit, stableSchema, stableUiSchema, values]);
|
|
89
|
+
const isDirty = useMemo(() => stringifyComparable(values) !== stringifyComparable(baselineValues), [baselineValues, values]);
|
|
90
|
+
return useMemo(() => ({
|
|
91
|
+
fields,
|
|
92
|
+
values,
|
|
93
|
+
baselineValues,
|
|
94
|
+
fieldErrors,
|
|
95
|
+
isSubmitting,
|
|
96
|
+
isDirty,
|
|
97
|
+
error,
|
|
98
|
+
setValue,
|
|
99
|
+
setValues,
|
|
100
|
+
reset,
|
|
101
|
+
validate,
|
|
102
|
+
submit,
|
|
103
|
+
}), [baselineValues, error, fieldErrors, fields, isDirty, isSubmitting, reset, setValue, setValues, submit, validate, values]);
|
|
104
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
import type { Table } from "../types.js";
|
|
3
|
+
export interface UseTableOptions {
|
|
4
|
+
client: LemmaClient;
|
|
5
|
+
podId?: string;
|
|
6
|
+
tableName: string;
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
autoLoad?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface UseTableResult {
|
|
11
|
+
table: Table | null;
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
error: Error | null;
|
|
14
|
+
refresh: () => Promise<Table | null>;
|
|
15
|
+
}
|
|
16
|
+
export declare function useTable({ client, podId, tableName, enabled, autoLoad, }: UseTableOptions): UseTableResult;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { normalizeError, resolvePodClient } from "./utils.js";
|
|
3
|
+
export function useTable({ client, podId, tableName, enabled = true, autoLoad = true, }) {
|
|
4
|
+
const [table, setTable] = useState(null);
|
|
5
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
6
|
+
const [error, setError] = useState(null);
|
|
7
|
+
const trimmedTableName = tableName.trim();
|
|
8
|
+
const isEnabled = enabled && trimmedTableName.length > 0;
|
|
9
|
+
const refresh = useCallback(async (signal) => {
|
|
10
|
+
if (!isEnabled) {
|
|
11
|
+
setTable(null);
|
|
12
|
+
setError(null);
|
|
13
|
+
setIsLoading(false);
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
setIsLoading(true);
|
|
17
|
+
setError(null);
|
|
18
|
+
try {
|
|
19
|
+
const scopedClient = resolvePodClient(client, podId);
|
|
20
|
+
const nextTable = await scopedClient.tables.get(trimmedTableName);
|
|
21
|
+
if (signal?.aborted)
|
|
22
|
+
return null;
|
|
23
|
+
setTable(nextTable);
|
|
24
|
+
return nextTable;
|
|
25
|
+
}
|
|
26
|
+
catch (refreshError) {
|
|
27
|
+
if (signal?.aborted)
|
|
28
|
+
return null;
|
|
29
|
+
const normalized = normalizeError(refreshError, "Failed to load table.");
|
|
30
|
+
setError(normalized);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
if (!signal?.aborted)
|
|
35
|
+
setIsLoading(false);
|
|
36
|
+
}
|
|
37
|
+
}, [client, isEnabled, podId, trimmedTableName]);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!isEnabled) {
|
|
40
|
+
setTable(null);
|
|
41
|
+
setError(null);
|
|
42
|
+
setIsLoading(false);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (!autoLoad)
|
|
46
|
+
return;
|
|
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
|
+
};
|
|
63
|
+
}, [autoLoad, isEnabled, refresh]);
|
|
64
|
+
return useMemo(() => ({
|
|
65
|
+
table,
|
|
66
|
+
isLoading,
|
|
67
|
+
error,
|
|
68
|
+
refresh,
|
|
69
|
+
}), [error, isLoading, refresh, table]);
|
|
70
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
import type { Table } from "../types.js";
|
|
3
|
+
export interface UseTablesOptions {
|
|
4
|
+
client: LemmaClient;
|
|
5
|
+
podId?: string;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
autoLoad?: boolean;
|
|
8
|
+
limit?: number;
|
|
9
|
+
pageToken?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface UseTablesResult {
|
|
12
|
+
tables: Table[];
|
|
13
|
+
total: number;
|
|
14
|
+
nextPageToken: string | null;
|
|
15
|
+
isLoading: boolean;
|
|
16
|
+
isLoadingMore: boolean;
|
|
17
|
+
error: Error | null;
|
|
18
|
+
refresh: (overrides?: {
|
|
19
|
+
limit?: number;
|
|
20
|
+
pageToken?: string;
|
|
21
|
+
}) => Promise<Table[]>;
|
|
22
|
+
loadMore: (overrides?: {
|
|
23
|
+
limit?: number;
|
|
24
|
+
}) => Promise<Table[]>;
|
|
25
|
+
}
|
|
26
|
+
export declare function useTables({ client, podId, enabled, autoLoad, limit, pageToken, }: UseTablesOptions): UseTablesResult;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { normalizeError, resolvePodClient } from "./utils.js";
|
|
3
|
+
export function useTables({ client, podId, enabled = true, autoLoad = true, limit = 100, pageToken, }) {
|
|
4
|
+
const [tables, setTables] = useState([]);
|
|
5
|
+
const [total, setTotal] = useState(0);
|
|
6
|
+
const [nextPageToken, setNextPageToken] = useState(null);
|
|
7
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
8
|
+
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
|
9
|
+
const [error, setError] = useState(null);
|
|
10
|
+
const refresh = useCallback(async (overrides = {}, signal) => {
|
|
11
|
+
if (!enabled) {
|
|
12
|
+
setTables([]);
|
|
13
|
+
setTotal(0);
|
|
14
|
+
setNextPageToken(null);
|
|
15
|
+
setError(null);
|
|
16
|
+
setIsLoading(false);
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
setIsLoading(true);
|
|
20
|
+
setError(null);
|
|
21
|
+
try {
|
|
22
|
+
const scopedClient = resolvePodClient(client, podId);
|
|
23
|
+
const response = await scopedClient.tables.list({
|
|
24
|
+
limit: overrides.limit ?? limit,
|
|
25
|
+
pageToken: overrides.pageToken ?? pageToken,
|
|
26
|
+
});
|
|
27
|
+
if (signal?.aborted)
|
|
28
|
+
return [];
|
|
29
|
+
const nextTables = response.items ?? [];
|
|
30
|
+
setTables(nextTables);
|
|
31
|
+
setTotal(response.total ?? nextTables.length);
|
|
32
|
+
setNextPageToken(response.next_page_token ?? null);
|
|
33
|
+
return nextTables;
|
|
34
|
+
}
|
|
35
|
+
catch (refreshError) {
|
|
36
|
+
if (signal?.aborted)
|
|
37
|
+
return [];
|
|
38
|
+
const normalized = normalizeError(refreshError, "Failed to load tables.");
|
|
39
|
+
setError(normalized);
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
if (!signal?.aborted)
|
|
44
|
+
setIsLoading(false);
|
|
45
|
+
}
|
|
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]);
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (!enabled) {
|
|
76
|
+
setTables([]);
|
|
77
|
+
setTotal(0);
|
|
78
|
+
setNextPageToken(null);
|
|
79
|
+
setError(null);
|
|
80
|
+
setIsLoading(false);
|
|
81
|
+
setIsLoadingMore(false);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!autoLoad)
|
|
85
|
+
return;
|
|
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
|
+
};
|
|
102
|
+
}, [autoLoad, enabled, refresh]);
|
|
103
|
+
return useMemo(() => ({
|
|
104
|
+
tables,
|
|
105
|
+
total,
|
|
106
|
+
nextPageToken,
|
|
107
|
+
isLoading,
|
|
108
|
+
isLoadingMore,
|
|
109
|
+
error,
|
|
110
|
+
refresh,
|
|
111
|
+
loadMore,
|
|
112
|
+
}), [error, isLoading, isLoadingMore, loadMore, nextPageToken, refresh, tables, total]);
|
|
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
|
-
|
|
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
|
|
96
|
-
const nextTask = await
|
|
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
|
|
114
|
-
const response = await
|
|
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
|
|
145
|
-
const stream = await
|
|
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
|
|
205
|
-
const created = await
|
|
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
|
|
225
|
-
const stopped = await
|
|
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;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
import type { RecordResponse } from "../types.js";
|
|
3
|
+
export interface UseUpdateRecordOptions {
|
|
4
|
+
client: LemmaClient;
|
|
5
|
+
podId?: string;
|
|
6
|
+
tableName: string;
|
|
7
|
+
recordId?: string | null;
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
onSuccess?: (record: Record<string, unknown>, response: RecordResponse) => void;
|
|
10
|
+
onError?: (error: unknown) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface UseUpdateRecordResult<TRecord extends Record<string, unknown> = Record<string, unknown>> {
|
|
13
|
+
updatedRecord: TRecord | null;
|
|
14
|
+
isSubmitting: boolean;
|
|
15
|
+
error: Error | null;
|
|
16
|
+
update: (data: Record<string, unknown>, overrides?: {
|
|
17
|
+
recordId?: string | null;
|
|
18
|
+
}) => Promise<TRecord | null>;
|
|
19
|
+
reset: () => void;
|
|
20
|
+
}
|
|
21
|
+
export declare function useUpdateRecord<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, recordId, enabled, onSuccess, onError, }: UseUpdateRecordOptions): UseUpdateRecordResult<TRecord>;
|