lemma-sdk 0.2.30 → 0.2.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +213 -51
  2. package/dist/react/index.d.ts +23 -1
  3. package/dist/react/index.js +11 -0
  4. package/dist/react/useAgentInputSchema.d.ts +19 -0
  5. package/dist/react/useAgentInputSchema.js +73 -0
  6. package/dist/react/useAgentRun.js +18 -20
  7. package/dist/react/useAgentRuns.d.ts +33 -0
  8. package/dist/react/useAgentRuns.js +149 -0
  9. package/dist/react/useAssistantRun.js +10 -9
  10. package/dist/react/useAssistantSession.js +21 -25
  11. package/dist/react/useBulkRecords.js +9 -16
  12. package/dist/react/useConversation.js +24 -8
  13. package/dist/react/useConversations.d.ts +4 -0
  14. package/dist/react/useConversations.js +49 -3
  15. package/dist/react/useCreateRecord.d.ts +33 -3
  16. package/dist/react/useCreateRecord.js +20 -17
  17. package/dist/react/useCurrentUser.d.ts +14 -0
  18. package/dist/react/useCurrentUser.js +68 -0
  19. package/dist/react/useDeleteRecord.js +9 -16
  20. package/dist/react/useFlowRunHistory.js +1 -5
  21. package/dist/react/useFlowSession.js +41 -33
  22. package/dist/react/useForeignKeyOptions.d.ts +18 -0
  23. package/dist/react/useForeignKeyOptions.js +26 -15
  24. package/dist/react/useFunctionRun.d.ts +36 -0
  25. package/dist/react/useFunctionRun.js +30 -0
  26. package/dist/react/useFunctionRuns.d.ts +33 -0
  27. package/dist/react/useFunctionRuns.js +149 -0
  28. package/dist/react/useFunctionSession.js +37 -29
  29. package/dist/react/useJoinedRecords.d.ts +57 -2
  30. package/dist/react/useJoinedRecords.js +77 -27
  31. package/dist/react/useMembers.d.ts +4 -0
  32. package/dist/react/useMembers.js +55 -16
  33. package/dist/react/useOrganizationMembers.d.ts +26 -0
  34. package/dist/react/useOrganizationMembers.js +113 -0
  35. package/dist/react/usePodAccess.d.ts +22 -0
  36. package/dist/react/usePodAccess.js +128 -0
  37. package/dist/react/useRecord.d.ts +16 -0
  38. package/dist/react/useRecord.js +24 -13
  39. package/dist/react/useRecordForm.d.ts +42 -3
  40. package/dist/react/useRecordForm.js +44 -24
  41. package/dist/react/useRecords.d.ts +2 -0
  42. package/dist/react/useRecords.js +62 -22
  43. package/dist/react/useReferencingRecords.d.ts +66 -0
  44. package/dist/react/useReferencingRecords.js +159 -0
  45. package/dist/react/useRelatedRecords.d.ts +17 -0
  46. package/dist/react/useRelatedRecords.js +28 -21
  47. package/dist/react/useReverseRelatedRecords.d.ts +21 -0
  48. package/dist/react/useReverseRelatedRecords.js +30 -21
  49. package/dist/react/useSchemaForm.js +1 -13
  50. package/dist/react/useTable.js +24 -13
  51. package/dist/react/useTables.d.ts +4 -0
  52. package/dist/react/useTables.js +57 -15
  53. package/dist/react/useTaskSession.js +11 -22
  54. package/dist/react/useUpdateRecord.d.ts +34 -3
  55. package/dist/react/useUpdateRecord.js +21 -17
  56. package/dist/react/useWorkflowResume.d.ts +18 -0
  57. package/dist/react/useWorkflowResume.js +45 -0
  58. package/dist/react/useWorkflowRun.d.ts +21 -0
  59. package/dist/react/useWorkflowRun.js +49 -0
  60. package/dist/react/useWorkflowRuns.d.ts +33 -0
  61. package/dist/react/useWorkflowRuns.js +149 -0
  62. package/dist/react/useWorkflowStart.js +20 -27
  63. package/dist/react/utils.d.ts +5 -0
  64. package/dist/react/utils.js +25 -0
  65. package/dist/types.d.ts +1 -0
  66. package/package.json +2 -4
  67. package/dist/react/components/AssistantChrome.d.ts +0 -86
  68. package/dist/react/components/AssistantChrome.js +0 -48
  69. package/dist/react/components/AssistantEmbedded.d.ts +0 -10
  70. package/dist/react/components/AssistantEmbedded.js +0 -15
  71. package/dist/react/components/AssistantExperience.d.ts +0 -96
  72. package/dist/react/components/AssistantExperience.js +0 -1294
  73. package/dist/react/components/assistant-types.d.ts +0 -80
  74. package/dist/react/components/assistant-types.js +0 -1
@@ -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;
@@ -0,0 +1,128 @@
1
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
+ import { ApiError } from "../http.js";
3
+ import { normalizeError, resolvePodId } from "./utils.js";
4
+ function isMissingAccessError(error) {
5
+ return error instanceof ApiError && (error.statusCode === 403 || error.statusCode === 404);
6
+ }
7
+ export function usePodAccess({ client, podId, enabled = true, autoLoad = true, }) {
8
+ const [status, setStatus] = useState("idle");
9
+ const [user, setUser] = useState(null);
10
+ const [member, setMember] = useState(null);
11
+ const [joinRequest, setJoinRequest] = useState(null);
12
+ const [isLoading, setIsLoading] = useState(false);
13
+ const [isRequestingAccess, setIsRequestingAccess] = useState(false);
14
+ const [error, setError] = useState(null);
15
+ const refresh = useCallback(async () => {
16
+ if (!enabled) {
17
+ setStatus("idle");
18
+ setUser(null);
19
+ setMember(null);
20
+ setJoinRequest(null);
21
+ setError(null);
22
+ setIsLoading(false);
23
+ return "idle";
24
+ }
25
+ setStatus("checking");
26
+ setIsLoading(true);
27
+ setError(null);
28
+ try {
29
+ const resolvedPodId = resolvePodId(client, podId);
30
+ const currentUser = await client.users.current();
31
+ setUser(currentUser);
32
+ try {
33
+ const nextMember = await client.podMembers.get(resolvedPodId, currentUser.id);
34
+ setMember(nextMember);
35
+ setJoinRequest(null);
36
+ setStatus("member");
37
+ return "member";
38
+ }
39
+ catch (membershipError) {
40
+ if (!isMissingAccessError(membershipError)) {
41
+ throw membershipError;
42
+ }
43
+ }
44
+ setMember(null);
45
+ try {
46
+ const request = await client.podJoinRequests.me(resolvedPodId);
47
+ setJoinRequest(request);
48
+ const nextStatus = request?.status === "PENDING" ? "pending" : "missing";
49
+ setStatus(nextStatus);
50
+ return nextStatus;
51
+ }
52
+ catch (joinRequestError) {
53
+ if (!isMissingAccessError(joinRequestError)) {
54
+ throw joinRequestError;
55
+ }
56
+ }
57
+ setJoinRequest(null);
58
+ setStatus("missing");
59
+ return "missing";
60
+ }
61
+ catch (refreshError) {
62
+ const normalized = normalizeError(refreshError, "Failed to check pod access.");
63
+ setError(normalized);
64
+ setStatus("error");
65
+ return "error";
66
+ }
67
+ finally {
68
+ setIsLoading(false);
69
+ }
70
+ }, [client, enabled, podId]);
71
+ const requestAccess = useCallback(async () => {
72
+ setIsRequestingAccess(true);
73
+ setError(null);
74
+ try {
75
+ const resolvedPodId = resolvePodId(client, podId);
76
+ const request = await client.podJoinRequests.create(resolvedPodId);
77
+ setJoinRequest(request);
78
+ setMember(null);
79
+ setStatus(request.status === "PENDING" ? "pending" : "missing");
80
+ return request;
81
+ }
82
+ catch (requestError) {
83
+ const normalized = normalizeError(requestError, "Failed to request pod access.");
84
+ setError(normalized);
85
+ setStatus("error");
86
+ throw normalized;
87
+ }
88
+ finally {
89
+ setIsRequestingAccess(false);
90
+ }
91
+ }, [client, podId]);
92
+ useEffect(() => {
93
+ if (!enabled) {
94
+ setStatus("idle");
95
+ setUser(null);
96
+ setMember(null);
97
+ setJoinRequest(null);
98
+ setError(null);
99
+ setIsLoading(false);
100
+ return;
101
+ }
102
+ if (!autoLoad)
103
+ return;
104
+ void refresh();
105
+ }, [autoLoad, enabled, refresh]);
106
+ return useMemo(() => ({
107
+ status,
108
+ hasAccess: status === "member",
109
+ user,
110
+ member,
111
+ joinRequest,
112
+ isLoading,
113
+ isRequestingAccess,
114
+ error,
115
+ refresh,
116
+ requestAccess,
117
+ }), [
118
+ error,
119
+ isLoading,
120
+ isRequestingAccess,
121
+ joinRequest,
122
+ member,
123
+ refresh,
124
+ requestAccess,
125
+ status,
126
+ user,
127
+ ]);
128
+ }
@@ -1,4 +1,20 @@
1
1
  import type { LemmaClient } from "../client.js";
2
+ /**
3
+ * React hook for fetching a single record by ID. Unwraps `.data`
4
+ * automatically so `record` is the plain object, not the API envelope.
5
+ *
6
+ * Perfect for detail panels — pair with `useRecords` for the list.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { record, isLoading } = useRecord({
11
+ * client,
12
+ * tableName: "issues",
13
+ * recordId: selectedId,
14
+ * });
15
+ * // record is the issue object directly (or null)
16
+ * ```
17
+ */
2
18
  export interface UseRecordOptions {
3
19
  client: LemmaClient;
4
20
  podId?: string;
@@ -1,14 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
- function resolvePodClient(client, podId) {
3
- if (!podId || podId === client.podId)
4
- return client;
5
- return client.withPod(podId);
6
- }
7
- function normalizeError(error, fallback) {
8
- if (error instanceof Error)
9
- return error;
10
- return new Error(fallback);
11
- }
2
+ import { normalizeError, resolvePodClient } from "./utils.js";
12
3
  export function useRecord({ client, podId, tableName, recordId = null, enabled = true, autoLoad = true, }) {
13
4
  const [record, setRecord] = useState(null);
14
5
  const [isLoading, setIsLoading] = useState(false);
@@ -16,7 +7,7 @@ export function useRecord({ client, podId, tableName, recordId = null, enabled =
16
7
  const trimmedTableName = tableName.trim();
17
8
  const trimmedRecordId = typeof recordId === "string" ? recordId.trim() : "";
18
9
  const isEnabled = enabled && trimmedTableName.length > 0 && trimmedRecordId.length > 0;
19
- const refresh = useCallback(async (overrides = {}) => {
10
+ const refresh = useCallback(async (overrides = {}, signal) => {
20
11
  const nextRecordId = typeof overrides.recordId === "string"
21
12
  ? overrides.recordId.trim()
22
13
  : trimmedRecordId;
@@ -31,17 +22,22 @@ export function useRecord({ client, podId, tableName, recordId = null, enabled =
31
22
  try {
32
23
  const scopedClient = resolvePodClient(client, podId);
33
24
  const response = await scopedClient.records.get(trimmedTableName, nextRecordId);
25
+ if (signal?.aborted)
26
+ return null;
34
27
  const nextRecord = (response.data ?? null);
35
28
  setRecord(nextRecord);
36
29
  return nextRecord;
37
30
  }
38
31
  catch (refreshError) {
32
+ if (signal?.aborted)
33
+ return null;
39
34
  const normalized = normalizeError(refreshError, "Failed to load record.");
40
35
  setError(normalized);
41
36
  return null;
42
37
  }
43
38
  finally {
44
- setIsLoading(false);
39
+ if (!signal?.aborted)
40
+ setIsLoading(false);
45
41
  }
46
42
  }, [client, enabled, podId, trimmedRecordId, trimmedTableName]);
47
43
  useEffect(() => {
@@ -53,7 +49,22 @@ export function useRecord({ client, podId, tableName, recordId = null, enabled =
53
49
  }
54
50
  if (!autoLoad)
55
51
  return;
56
- void refresh();
52
+ const controller = new AbortController();
53
+ let cancelled = false;
54
+ (async () => {
55
+ try {
56
+ await refresh({}, controller.signal);
57
+ }
58
+ catch {
59
+ if (!cancelled) {
60
+ setError(normalizeError(new Error("Failed to load record."), "Failed to load record."));
61
+ }
62
+ }
63
+ })();
64
+ return () => {
65
+ cancelled = true;
66
+ controller.abort();
67
+ };
57
68
  }, [autoLoad, isEnabled, refresh]);
58
69
  return useMemo(() => ({
59
70
  record,
@@ -1,7 +1,36 @@
1
1
  import type { LemmaClient } from "../client.js";
2
2
  import { type RecordSchemaField } from "../record-form.js";
3
- import type { RecordResponse } from "../types.js";
3
+ import type { FunctionRun, RecordResponse } from "../types.js";
4
4
  import { type UseRecordSchemaResult } from "./useRecordSchema.js";
5
+ /**
6
+ * React hook for schema-driven record forms with validation, dirty tracking,
7
+ * and create/update auto-detection.
8
+ *
9
+ * Supports two submit modes:
10
+ * - `"direct"` (default): persists via `records.create` / `records.update`.
11
+ * - `"function"`: persists via `functions.runs.create`, routing the form
12
+ * payload through a pod function that may enforce business logic
13
+ * (e.g. auto-generating identifiers, logging history).
14
+ *
15
+ * @example Direct create/update form
16
+ * ```tsx
17
+ * const form = useRecordForm({ client, tableName: "issues" });
18
+ * // form.fields → all schema fields, form.editableFields → user-fillable fields
19
+ * await form.submit(); // calls records.create or records.update
20
+ * ```
21
+ *
22
+ * @example Function-backed form
23
+ * ```tsx
24
+ * const form = useRecordForm({
25
+ * client,
26
+ * tableName: "issues",
27
+ * submitVia: "function",
28
+ * submitFunctionName: "create-issue",
29
+ * hiddenFields: ["identifier", "created_at"],
30
+ * });
31
+ * await form.submit(); // calls functions.runs.create("create-issue", { input: payload })
32
+ * ```
33
+ */
5
34
  export interface UseRecordFormOptions {
6
35
  client: LemmaClient;
7
36
  podId?: string;
@@ -11,7 +40,17 @@ export interface UseRecordFormOptions {
11
40
  mode?: "auto" | "create" | "update";
12
41
  enabled?: boolean;
13
42
  autoLoad?: boolean;
14
- onSubmitSuccess?: (record: Record<string, unknown>, response: RecordResponse) => void;
43
+ /** Field names to include in `fields` and `editableFields`. Takes precedence over `hiddenFields`. */
44
+ visibleFields?: string[];
45
+ /** Field names to exclude from `fields` and `editableFields`. Ignored for any field also listed in `visibleFields`. */
46
+ hiddenFields?: string[];
47
+ /** How the form persists data. `"direct"` calls `records.create`/`records.update`. `"function"` calls `functions.runs.create`. */
48
+ submitVia?: "direct" | "function";
49
+ /** Function name to run when `submitVia` is `"function"`. Required when `submitVia` is `"function"`. */
50
+ submitFunctionName?: string;
51
+ /** Transforms the form payload before passing it as function input. Receives the validated payload, returns the function input object. */
52
+ submitFunctionInput?: (payload: Record<string, unknown>) => Record<string, unknown>;
53
+ onSubmitSuccess?: (record: Record<string, unknown>, response: RecordResponse | FunctionRun) => void;
15
54
  onError?: (error: unknown) => void;
16
55
  }
17
56
  export interface UseRecordFormResult {
@@ -39,4 +78,4 @@ export interface UseRecordFormResult {
39
78
  mode?: "create" | "update";
40
79
  }) => Promise<Record<string, unknown> | null>;
41
80
  }
42
- export declare function useRecordForm({ client, podId, tableName, recordId, initialValues, mode, enabled, autoLoad, onSubmitSuccess, onError, }: UseRecordFormOptions): UseRecordFormResult;
81
+ export declare function useRecordForm({ client, podId, tableName, recordId, initialValues, mode, enabled, autoLoad, visibleFields, hiddenFields, submitVia, submitFunctionName, submitFunctionInput, onSubmitSuccess, onError, }: UseRecordFormOptions): UseRecordFormResult;
@@ -1,26 +1,9 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { buildRecordFormValues, buildRecordPayload, } from "../record-form.js";
3
+ import { normalizeError, resolvePodClient, stringifyComparable } from "./utils.js";
3
4
  import { useRecordSchema, } from "./useRecordSchema.js";
4
5
  const EMPTY_VALUES = {};
5
- function normalizeError(error, fallback) {
6
- if (error instanceof Error)
7
- return error;
8
- return new Error(fallback);
9
- }
10
- function resolvePodClient(client, podId) {
11
- if (!podId || podId === client.podId)
12
- return client;
13
- return client.withPod(podId);
14
- }
15
- function stringifyComparable(value) {
16
- try {
17
- return JSON.stringify(value);
18
- }
19
- catch {
20
- return String(value);
21
- }
22
- }
23
- export function useRecordForm({ client, podId, tableName, recordId = null, initialValues = EMPTY_VALUES, mode = "auto", enabled = true, autoLoad = true, onSubmitSuccess, onError, }) {
6
+ export function useRecordForm({ client, podId, tableName, recordId = null, initialValues = EMPTY_VALUES, mode = "auto", enabled = true, autoLoad = true, visibleFields, hiddenFields, submitVia = "direct", submitFunctionName, submitFunctionInput, onSubmitSuccess, onError, }) {
24
7
  const schema = useRecordSchema({
25
8
  client,
26
9
  podId,
@@ -28,6 +11,30 @@ export function useRecordForm({ client, podId, tableName, recordId = null, initi
28
11
  enabled,
29
12
  autoLoad,
30
13
  });
14
+ const visibleSet = useMemo(() => visibleFields ? new Set(visibleFields) : null, [visibleFields]);
15
+ const hiddenSet = useMemo(() => hiddenFields ? new Set(hiddenFields) : null, [hiddenFields]);
16
+ const filteredFields = useMemo(() => {
17
+ if (!visibleSet && !hiddenSet)
18
+ return schema.fields;
19
+ return schema.fields.filter((field) => {
20
+ if (visibleSet)
21
+ return visibleSet.has(field.name);
22
+ if (hiddenSet)
23
+ return !hiddenSet.has(field.name);
24
+ return true;
25
+ });
26
+ }, [hiddenSet, schema.fields, visibleSet]);
27
+ const filteredEditableFields = useMemo(() => {
28
+ if (!visibleSet && !hiddenSet)
29
+ return schema.editableFields;
30
+ return schema.editableFields.filter((field) => {
31
+ if (visibleSet)
32
+ return visibleSet.has(field.name);
33
+ if (hiddenSet)
34
+ return !hiddenSet.has(field.name);
35
+ return true;
36
+ });
37
+ }, [hiddenSet, schema.editableFields, visibleSet]);
31
38
  const [record, setRecord] = useState(null);
32
39
  const [values, setValuesState] = useState({});
33
40
  const [baselineValues, setBaselineValues] = useState({});
@@ -164,6 +171,19 @@ export function useRecordForm({ client, podId, tableName, recordId = null, initi
164
171
  setRecordError(null);
165
172
  try {
166
173
  const scopedClient = resolvePodClient(client, podId);
174
+ if (submitVia === "function") {
175
+ const functionName = submitFunctionName ?? tableName;
176
+ const functionInput = submitFunctionInput ? submitFunctionInput(payload.data) : payload.data;
177
+ const run = await scopedClient.functions.runs.create(functionName, { input: functionInput });
178
+ const nextRecord = run.output_data ?? { id: run.id, ...functionInput };
179
+ setRecord(nextRecord);
180
+ hydrateValues({
181
+ ...(nextRecord ?? {}),
182
+ ...stableInitialValues,
183
+ });
184
+ onSubmitSuccess?.(nextRecord ?? {}, run);
185
+ return nextRecord;
186
+ }
167
187
  const response = resolvedMode === "update" && recordId
168
188
  ? await scopedClient.records.update(tableName, recordId, payload.data)
169
189
  : await scopedClient.records.create(tableName, payload.data);
@@ -185,14 +205,14 @@ export function useRecordForm({ client, podId, tableName, recordId = null, initi
185
205
  finally {
186
206
  setIsSubmitting(false);
187
207
  }
188
- }, [client, hydrateValues, mode, onError, onSubmitSuccess, podId, recordId, schemaTable, stableInitialValues, tableName, values]);
208
+ }, [client, hydrateValues, mode, onError, onSubmitSuccess, podId, recordId, schemaTable, stableInitialValues, submitFunctionInput, submitFunctionName, submitVia, tableName, values]);
189
209
  const isDirty = useMemo(() => {
190
210
  return stringifyComparable(values) !== stringifyComparable(baselineValues);
191
211
  }, [baselineValues, values]);
192
212
  return useMemo(() => ({
193
213
  table: schema.table,
194
- fields: schema.fields,
195
- editableFields: schema.editableFields,
214
+ fields: filteredFields,
215
+ editableFields: filteredEditableFields,
196
216
  defaults: schema.defaults,
197
217
  values,
198
218
  baselineValues,
@@ -214,6 +234,8 @@ export function useRecordForm({ client, podId, tableName, recordId = null, initi
214
234
  }), [
215
235
  baselineValues,
216
236
  fieldErrors,
237
+ filteredEditableFields,
238
+ filteredFields,
217
239
  isDirty,
218
240
  isLoadingRecord,
219
241
  isSubmitting,
@@ -223,9 +245,7 @@ export function useRecordForm({ client, podId, tableName, recordId = null, initi
223
245
  refreshRecord,
224
246
  reset,
225
247
  schema.defaults,
226
- schema.editableFields,
227
248
  schema.error,
228
- schema.fields,
229
249
  schema.isLoading,
230
250
  schema.refresh,
231
251
  schema.table,
@@ -12,7 +12,9 @@ export interface UseRecordsResult<TRecord extends Record<string, unknown> = Reco
12
12
  total: number;
13
13
  nextPageToken: string | null;
14
14
  isLoading: boolean;
15
+ isLoadingMore: boolean;
15
16
  error: Error | null;
16
17
  refresh: (overrides?: Partial<ListRecordsOptions>) => Promise<TRecord[]>;
18
+ loadMore: (overrides?: Partial<ListRecordsOptions>) => Promise<TRecord[]>;
17
19
  }
18
20
  export declare function useRecords<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, filters, sort, limit, pageToken, offset, sortBy, order, params, enabled, autoLoad, }: UseRecordsOptions): UseRecordsResult<TRecord>;