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,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
|
+
}
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from "react";
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
import { isTerminalFunctionStatus, normalizeRunStatus, sleep } from "../run-utils.js";
|
|
3
|
-
|
|
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 resolveFunctionName(base, override) {
|
|
11
5
|
const resolved = override ?? base;
|
|
12
6
|
if (!resolved) {
|
|
@@ -14,17 +8,16 @@ function resolveFunctionName(base, override) {
|
|
|
14
8
|
}
|
|
15
9
|
return resolved;
|
|
16
10
|
}
|
|
17
|
-
function normalizeError(error, fallback) {
|
|
18
|
-
if (error instanceof Error)
|
|
19
|
-
return error;
|
|
20
|
-
return new Error(fallback);
|
|
21
|
-
}
|
|
22
11
|
export function useFunctionSession({ client, podId, functionName, runId: externalRunId = null, autoPoll = true, pollIntervalMs = 2000, onRun, onError, }) {
|
|
23
12
|
const [runId, setRunIdState] = useState(externalRunId);
|
|
24
13
|
const [run, setRun] = useState(null);
|
|
25
14
|
const [status, setStatus] = useState(undefined);
|
|
26
15
|
const [isPolling, setIsPolling] = useState(false);
|
|
27
16
|
const [error, setError] = useState(null);
|
|
17
|
+
const onRunRef = useRef(onRun);
|
|
18
|
+
const onErrorRef = useRef(onError);
|
|
19
|
+
useEffect(() => { onRunRef.current = onRun; }, [onRun]);
|
|
20
|
+
useEffect(() => { onErrorRef.current = onError; }, [onError]);
|
|
28
21
|
const setRunId = useCallback((nextRunId) => {
|
|
29
22
|
setRunIdState(nextRunId);
|
|
30
23
|
if (!nextRunId) {
|
|
@@ -44,27 +37,27 @@ export function useFunctionSession({ client, podId, functionName, runId: externa
|
|
|
44
37
|
if (!id)
|
|
45
38
|
return null;
|
|
46
39
|
try {
|
|
47
|
-
client
|
|
40
|
+
const scopedClient = resolvePodClient(client, resolvePodId(client, podId));
|
|
48
41
|
const name = resolveFunctionName(functionName);
|
|
49
|
-
const nextRun = await
|
|
42
|
+
const nextRun = await scopedClient.functions.runs.get(name, id);
|
|
50
43
|
setRun(nextRun);
|
|
51
44
|
const nextStatus = normalizeRunStatus(nextRun.status);
|
|
52
45
|
setStatus(nextStatus);
|
|
53
|
-
|
|
46
|
+
onRunRef.current?.(nextRun);
|
|
54
47
|
return nextRun;
|
|
55
48
|
}
|
|
56
49
|
catch (refreshError) {
|
|
57
50
|
const normalized = normalizeError(refreshError, "Failed to fetch function run.");
|
|
58
51
|
setError(normalized);
|
|
59
|
-
|
|
52
|
+
onErrorRef.current?.(refreshError);
|
|
60
53
|
return null;
|
|
61
54
|
}
|
|
62
|
-
}, [client, functionName,
|
|
55
|
+
}, [client, functionName, podId, runId]);
|
|
63
56
|
const listHistory = useCallback(async (options = {}) => {
|
|
64
57
|
try {
|
|
65
|
-
client
|
|
58
|
+
const scopedClient = resolvePodClient(client, resolvePodId(client, podId));
|
|
66
59
|
const name = resolveFunctionName(functionName, options.functionName);
|
|
67
|
-
const response = await
|
|
60
|
+
const response = await scopedClient.functions.runs.list(name, {
|
|
68
61
|
limit: options.limit,
|
|
69
62
|
pageToken: options.pageToken,
|
|
70
63
|
});
|
|
@@ -73,32 +66,47 @@ export function useFunctionSession({ client, podId, functionName, runId: externa
|
|
|
73
66
|
catch (listError) {
|
|
74
67
|
const normalized = normalizeError(listError, "Failed to list function runs.");
|
|
75
68
|
setError(normalized);
|
|
76
|
-
|
|
69
|
+
onErrorRef.current?.(listError);
|
|
77
70
|
return [];
|
|
78
71
|
}
|
|
79
|
-
}, [client, functionName,
|
|
72
|
+
}, [client, functionName, podId]);
|
|
80
73
|
const start = useCallback(async (options = {}) => {
|
|
81
74
|
setError(null);
|
|
82
|
-
client
|
|
75
|
+
const scopedClient = resolvePodClient(client, resolvePodId(client, podId));
|
|
83
76
|
const name = resolveFunctionName(functionName, options.functionName);
|
|
84
|
-
const created = await
|
|
77
|
+
const created = await scopedClient.functions.runs.create(name, {
|
|
85
78
|
input: options.input,
|
|
86
79
|
});
|
|
87
80
|
setRun(created);
|
|
88
81
|
setRunIdState(created.id);
|
|
89
82
|
const nextStatus = normalizeRunStatus(created.status);
|
|
90
83
|
setStatus(nextStatus);
|
|
91
|
-
|
|
84
|
+
onRunRef.current?.(created);
|
|
92
85
|
if (options.connect !== false) {
|
|
93
86
|
await refresh(created.id);
|
|
94
87
|
}
|
|
95
88
|
return created;
|
|
96
|
-
}, [client, functionName,
|
|
89
|
+
}, [client, functionName, podId, refresh]);
|
|
97
90
|
useEffect(() => {
|
|
98
91
|
if (!runId) {
|
|
99
92
|
return;
|
|
100
93
|
}
|
|
101
|
-
|
|
94
|
+
const controller = new AbortController();
|
|
95
|
+
let cancelled = false;
|
|
96
|
+
(async () => {
|
|
97
|
+
try {
|
|
98
|
+
await refresh(runId);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
if (!cancelled) {
|
|
102
|
+
// refresh handles errors internally
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})();
|
|
106
|
+
return () => {
|
|
107
|
+
cancelled = true;
|
|
108
|
+
controller.abort();
|
|
109
|
+
};
|
|
102
110
|
}, [refresh, runId]);
|
|
103
111
|
useEffect(() => {
|
|
104
112
|
if (!autoPoll || !runId) {
|
|
@@ -132,14 +140,14 @@ export function useFunctionSession({ client, podId, functionName, runId: externa
|
|
|
132
140
|
void loop().catch((pollError) => {
|
|
133
141
|
const normalized = normalizeError(pollError, "Failed while polling function run.");
|
|
134
142
|
setError(normalized);
|
|
135
|
-
|
|
143
|
+
onErrorRef.current?.(pollError);
|
|
136
144
|
setIsPolling(false);
|
|
137
145
|
});
|
|
138
146
|
return () => {
|
|
139
147
|
active = false;
|
|
140
148
|
abortController.abort();
|
|
141
149
|
};
|
|
142
|
-
}, [autoPoll,
|
|
150
|
+
}, [autoPoll, pollIntervalMs, refresh, runId]);
|
|
143
151
|
return {
|
|
144
152
|
runId,
|
|
145
153
|
run,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
import { type JoinedRecordsQueryDefinition } from "../datastore-query.js";
|
|
3
|
+
export interface UseJoinedRecordsOptions {
|
|
4
|
+
client: LemmaClient;
|
|
5
|
+
podId?: string;
|
|
6
|
+
query: JoinedRecordsQueryDefinition;
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
autoLoad?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface UseJoinedRecordsResult<TRecord extends Record<string, unknown> = Record<string, unknown>> {
|
|
11
|
+
records: TRecord[];
|
|
12
|
+
total: number;
|
|
13
|
+
sql: string;
|
|
14
|
+
isLoading: boolean;
|
|
15
|
+
error: Error | null;
|
|
16
|
+
refresh: () => Promise<TRecord[]>;
|
|
17
|
+
}
|
|
18
|
+
export declare function useJoinedRecords<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, query, enabled, autoLoad, }: UseJoinedRecordsOptions): UseJoinedRecordsResult<TRecord>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { buildJoinedRecordsQuery } from "../datastore-query.js";
|
|
3
|
+
import { normalizeError, resolvePodId, stringifyComparable } from "./utils.js";
|
|
4
|
+
export function useJoinedRecords({ client, podId, query, enabled = true, autoLoad = true, }) {
|
|
5
|
+
const [records, setRecords] = useState([]);
|
|
6
|
+
const [total, setTotal] = useState(0);
|
|
7
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
8
|
+
const [error, setError] = useState(null);
|
|
9
|
+
const queryKey = stringifyComparable(query);
|
|
10
|
+
const stableQuery = useMemo(() => query, [queryKey]);
|
|
11
|
+
const sql = useMemo(() => buildJoinedRecordsQuery(stableQuery), [stableQuery]);
|
|
12
|
+
const refresh = useCallback(async (signal) => {
|
|
13
|
+
if (!enabled) {
|
|
14
|
+
setRecords([]);
|
|
15
|
+
setTotal(0);
|
|
16
|
+
setError(null);
|
|
17
|
+
setIsLoading(false);
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
setIsLoading(true);
|
|
21
|
+
setError(null);
|
|
22
|
+
try {
|
|
23
|
+
const resolvedPodId = resolvePodId(client, podId);
|
|
24
|
+
const scopedClient = resolvedPodId === client.podId ? client : client.withPod(resolvedPodId);
|
|
25
|
+
const response = await scopedClient.datastore.query(sql);
|
|
26
|
+
if (signal?.aborted)
|
|
27
|
+
return [];
|
|
28
|
+
const nextRecords = (response.items ?? []);
|
|
29
|
+
setRecords(nextRecords);
|
|
30
|
+
setTotal(response.total ?? nextRecords.length);
|
|
31
|
+
return nextRecords;
|
|
32
|
+
}
|
|
33
|
+
catch (refreshError) {
|
|
34
|
+
if (signal?.aborted)
|
|
35
|
+
return [];
|
|
36
|
+
const normalized = normalizeError(refreshError, "Failed to load joined records.");
|
|
37
|
+
setError(normalized);
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
if (!signal?.aborted)
|
|
42
|
+
setIsLoading(false);
|
|
43
|
+
}
|
|
44
|
+
}, [client, enabled, podId, sql]);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!enabled) {
|
|
47
|
+
setRecords([]);
|
|
48
|
+
setTotal(0);
|
|
49
|
+
setError(null);
|
|
50
|
+
setIsLoading(false);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!autoLoad)
|
|
54
|
+
return;
|
|
55
|
+
const controller = new AbortController();
|
|
56
|
+
let cancelled = false;
|
|
57
|
+
(async () => {
|
|
58
|
+
try {
|
|
59
|
+
await refresh(controller.signal);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
if (!cancelled) {
|
|
63
|
+
setError(normalizeError(new Error("Failed to load joined records."), "Failed to load joined records."));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})();
|
|
67
|
+
return () => {
|
|
68
|
+
cancelled = true;
|
|
69
|
+
controller.abort();
|
|
70
|
+
};
|
|
71
|
+
}, [autoLoad, enabled, refresh]);
|
|
72
|
+
return useMemo(() => ({
|
|
73
|
+
records,
|
|
74
|
+
total,
|
|
75
|
+
sql,
|
|
76
|
+
isLoading,
|
|
77
|
+
error,
|
|
78
|
+
refresh,
|
|
79
|
+
}), [error, isLoading, records, refresh, sql, total]);
|
|
80
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
import type { PodMember } from "../types.js";
|
|
3
|
+
export interface UseMembersOptions {
|
|
4
|
+
client: LemmaClient;
|
|
5
|
+
podId?: string;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
autoLoad?: boolean;
|
|
8
|
+
limit?: number;
|
|
9
|
+
pageToken?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface UseMembersResult {
|
|
12
|
+
members: PodMember[];
|
|
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<PodMember[]>;
|
|
22
|
+
loadMore: (overrides?: {
|
|
23
|
+
limit?: number;
|
|
24
|
+
}) => Promise<PodMember[]>;
|
|
25
|
+
}
|
|
26
|
+
export declare function useMembers({ client, podId, enabled, autoLoad, limit, pageToken, }: UseMembersOptions): UseMembersResult;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { normalizeError, resolvePodId } from "./utils.js";
|
|
3
|
+
export function useMembers({ client, podId, enabled = true, autoLoad = true, limit = 100, pageToken, }) {
|
|
4
|
+
const [members, setMembers] = 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
|
+
return [];
|
|
13
|
+
setIsLoading(true);
|
|
14
|
+
setError(null);
|
|
15
|
+
try {
|
|
16
|
+
const resolvedPodId = resolvePodId(client, podId);
|
|
17
|
+
const response = await client.podMembers.list(resolvedPodId, {
|
|
18
|
+
limit: overrides.limit ?? limit,
|
|
19
|
+
pageToken: overrides.pageToken ?? pageToken,
|
|
20
|
+
});
|
|
21
|
+
if (signal?.aborted)
|
|
22
|
+
return [];
|
|
23
|
+
const nextMembers = response.items ?? [];
|
|
24
|
+
setMembers(nextMembers);
|
|
25
|
+
setTotal(response.total ?? nextMembers.length);
|
|
26
|
+
setNextPageToken(response.next_page_token ?? null);
|
|
27
|
+
return nextMembers;
|
|
28
|
+
}
|
|
29
|
+
catch (refreshError) {
|
|
30
|
+
if (signal?.aborted)
|
|
31
|
+
return [];
|
|
32
|
+
const normalized = normalizeError(refreshError, "Failed to load pod members.");
|
|
33
|
+
setError(normalized);
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
if (!signal?.aborted)
|
|
38
|
+
setIsLoading(false);
|
|
39
|
+
}
|
|
40
|
+
}, [client, enabled, limit, pageToken, podId]);
|
|
41
|
+
const loadMore = useCallback(async (overrides = {}) => {
|
|
42
|
+
if (!enabled || !nextPageToken || isLoading || isLoadingMore) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
setIsLoadingMore(true);
|
|
46
|
+
setError(null);
|
|
47
|
+
try {
|
|
48
|
+
const resolvedPodId = resolvePodId(client, podId);
|
|
49
|
+
const response = await client.podMembers.list(resolvedPodId, {
|
|
50
|
+
limit: overrides.limit ?? limit,
|
|
51
|
+
pageToken: nextPageToken,
|
|
52
|
+
});
|
|
53
|
+
const moreMembers = response.items ?? [];
|
|
54
|
+
setMembers((previous) => [...previous, ...moreMembers]);
|
|
55
|
+
setTotal(response.total ?? members.length + moreMembers.length);
|
|
56
|
+
setNextPageToken(response.next_page_token ?? null);
|
|
57
|
+
return moreMembers;
|
|
58
|
+
}
|
|
59
|
+
catch (loadError) {
|
|
60
|
+
const normalized = normalizeError(loadError, "Failed to load more pod members.");
|
|
61
|
+
setError(normalized);
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
setIsLoadingMore(false);
|
|
66
|
+
}
|
|
67
|
+
}, [client, enabled, isLoading, isLoadingMore, limit, members.length, nextPageToken, podId]);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (!enabled || !autoLoad)
|
|
70
|
+
return;
|
|
71
|
+
const controller = new AbortController();
|
|
72
|
+
let cancelled = false;
|
|
73
|
+
(async () => {
|
|
74
|
+
try {
|
|
75
|
+
await refresh({}, controller.signal);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
if (!cancelled) {
|
|
79
|
+
setError(normalizeError(new Error("Failed to load pod members."), "Failed to load pod members."));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
83
|
+
return () => {
|
|
84
|
+
cancelled = true;
|
|
85
|
+
controller.abort();
|
|
86
|
+
};
|
|
87
|
+
}, [autoLoad, enabled, refresh]);
|
|
88
|
+
return useMemo(() => ({
|
|
89
|
+
members,
|
|
90
|
+
total,
|
|
91
|
+
nextPageToken,
|
|
92
|
+
isLoading,
|
|
93
|
+
isLoadingMore,
|
|
94
|
+
error,
|
|
95
|
+
refresh,
|
|
96
|
+
loadMore,
|
|
97
|
+
}), [error, isLoading, isLoadingMore, loadMore, members, nextPageToken, refresh, total]);
|
|
98
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
import type { OrganizationMember } from "../types.js";
|
|
3
|
+
export interface UseOrganizationMembersOptions {
|
|
4
|
+
client: LemmaClient;
|
|
5
|
+
organizationId: string;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
autoLoad?: boolean;
|
|
8
|
+
limit?: number;
|
|
9
|
+
pageToken?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface UseOrganizationMembersResult {
|
|
12
|
+
members: OrganizationMember[];
|
|
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<OrganizationMember[]>;
|
|
22
|
+
loadMore: (overrides?: {
|
|
23
|
+
limit?: number;
|
|
24
|
+
}) => Promise<OrganizationMember[]>;
|
|
25
|
+
}
|
|
26
|
+
export declare function useOrganizationMembers({ client, organizationId, enabled, autoLoad, limit, pageToken, }: UseOrganizationMembersOptions): UseOrganizationMembersResult;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { normalizeError } from "./utils.js";
|
|
3
|
+
export function useOrganizationMembers({ client, organizationId, enabled = true, autoLoad = true, limit = 100, pageToken, }) {
|
|
4
|
+
const [members, setMembers] = 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 trimmedOrganizationId = organizationId.trim();
|
|
11
|
+
const isEnabled = enabled && trimmedOrganizationId.length > 0;
|
|
12
|
+
const refresh = useCallback(async (overrides = {}, signal) => {
|
|
13
|
+
if (!isEnabled) {
|
|
14
|
+
setMembers([]);
|
|
15
|
+
setTotal(0);
|
|
16
|
+
setNextPageToken(null);
|
|
17
|
+
setError(null);
|
|
18
|
+
setIsLoading(false);
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
setIsLoading(true);
|
|
22
|
+
setError(null);
|
|
23
|
+
try {
|
|
24
|
+
const response = await client.organizations.members.list(trimmedOrganizationId, {
|
|
25
|
+
limit: overrides.limit ?? limit,
|
|
26
|
+
pageToken: overrides.pageToken ?? pageToken,
|
|
27
|
+
});
|
|
28
|
+
if (signal?.aborted)
|
|
29
|
+
return [];
|
|
30
|
+
const nextMembers = response.items ?? [];
|
|
31
|
+
setMembers(nextMembers);
|
|
32
|
+
setTotal(response.total ?? nextMembers.length);
|
|
33
|
+
setNextPageToken(response.next_page_token ?? null);
|
|
34
|
+
return nextMembers;
|
|
35
|
+
}
|
|
36
|
+
catch (refreshError) {
|
|
37
|
+
if (signal?.aborted)
|
|
38
|
+
return [];
|
|
39
|
+
const normalized = normalizeError(refreshError, "Failed to load organization members.");
|
|
40
|
+
setError(normalized);
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
if (!signal?.aborted)
|
|
45
|
+
setIsLoading(false);
|
|
46
|
+
}
|
|
47
|
+
}, [client, isEnabled, limit, pageToken, trimmedOrganizationId]);
|
|
48
|
+
const loadMore = useCallback(async (overrides = {}) => {
|
|
49
|
+
if (!isEnabled || !nextPageToken || isLoading || isLoadingMore) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
setIsLoadingMore(true);
|
|
53
|
+
setError(null);
|
|
54
|
+
try {
|
|
55
|
+
const response = await client.organizations.members.list(trimmedOrganizationId, {
|
|
56
|
+
limit: overrides.limit ?? limit,
|
|
57
|
+
pageToken: nextPageToken,
|
|
58
|
+
});
|
|
59
|
+
const moreMembers = response.items ?? [];
|
|
60
|
+
setMembers((previous) => [...previous, ...moreMembers]);
|
|
61
|
+
setTotal(response.total ?? members.length + moreMembers.length);
|
|
62
|
+
setNextPageToken(response.next_page_token ?? null);
|
|
63
|
+
return moreMembers;
|
|
64
|
+
}
|
|
65
|
+
catch (loadError) {
|
|
66
|
+
const normalized = normalizeError(loadError, "Failed to load more organization members.");
|
|
67
|
+
setError(normalized);
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
setIsLoadingMore(false);
|
|
72
|
+
}
|
|
73
|
+
}, [client, isEnabled, isLoading, isLoadingMore, limit, members.length, nextPageToken, trimmedOrganizationId]);
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (!isEnabled) {
|
|
76
|
+
setMembers([]);
|
|
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 organization members."), "Failed to load organization members."));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
})();
|
|
98
|
+
return () => {
|
|
99
|
+
cancelled = true;
|
|
100
|
+
controller.abort();
|
|
101
|
+
};
|
|
102
|
+
}, [autoLoad, isEnabled, refresh]);
|
|
103
|
+
return useMemo(() => ({
|
|
104
|
+
members,
|
|
105
|
+
total,
|
|
106
|
+
nextPageToken,
|
|
107
|
+
isLoading,
|
|
108
|
+
isLoadingMore,
|
|
109
|
+
error,
|
|
110
|
+
refresh,
|
|
111
|
+
loadMore,
|
|
112
|
+
}), [error, isLoading, isLoadingMore, loadMore, members, nextPageToken, refresh, total]);
|
|
113
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
import type { PodJoinRequest, PodMember, User } from "../types.js";
|
|
3
|
+
export type PodAccessStatus = "idle" | "checking" | "member" | "missing" | "pending" | "error";
|
|
4
|
+
export interface UsePodAccessOptions {
|
|
5
|
+
client: LemmaClient;
|
|
6
|
+
podId?: string;
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
autoLoad?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface UsePodAccessResult {
|
|
11
|
+
status: PodAccessStatus;
|
|
12
|
+
hasAccess: boolean;
|
|
13
|
+
user: User | null;
|
|
14
|
+
member: PodMember | null;
|
|
15
|
+
joinRequest: PodJoinRequest | null;
|
|
16
|
+
isLoading: boolean;
|
|
17
|
+
isRequestingAccess: boolean;
|
|
18
|
+
error: Error | null;
|
|
19
|
+
refresh: () => Promise<PodAccessStatus>;
|
|
20
|
+
requestAccess: () => Promise<PodJoinRequest>;
|
|
21
|
+
}
|
|
22
|
+
export declare function usePodAccess({ client, podId, enabled, autoLoad, }: UsePodAccessOptions): UsePodAccessResult;
|