lemma-sdk 0.2.31 → 0.2.33

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 (45) hide show
  1. package/README.md +138 -52
  2. package/dist/browser/lemma-client.js +23 -8
  3. package/dist/namespaces/desks.d.ts +5 -2
  4. package/dist/namespaces/desks.js +5 -2
  5. package/dist/namespaces/files.d.ts +11 -0
  6. package/dist/namespaces/files.js +12 -0
  7. package/dist/openapi_client/models/DeskBundleUploadRequest.d.ts +1 -1
  8. package/dist/openapi_client/services/DesksService.d.ts +4 -4
  9. package/dist/openapi_client/services/DesksService.js +6 -6
  10. package/dist/openapi_client/services/PublicDesksService.d.ts +2 -2
  11. package/dist/openapi_client/services/PublicDesksService.js +3 -3
  12. package/dist/react/index.d.ts +13 -1
  13. package/dist/react/index.js +6 -0
  14. package/dist/react/useAssistantController.js +82 -37
  15. package/dist/react/useAssistantRuntime.js +8 -4
  16. package/dist/react/useAssistantSession.js +44 -2
  17. package/dist/react/useConversationMessages.js +19 -2
  18. package/dist/react/useCreateRecord.d.ts +33 -3
  19. package/dist/react/useCreateRecord.js +12 -2
  20. package/dist/react/useFile.d.ts +18 -0
  21. package/dist/react/useFile.js +58 -0
  22. package/dist/react/useFilePreview.d.ts +23 -0
  23. package/dist/react/useFilePreview.js +76 -0
  24. package/dist/react/useFileSearch.d.ts +26 -0
  25. package/dist/react/useFileSearch.js +64 -0
  26. package/dist/react/useFileTree.d.ts +21 -0
  27. package/dist/react/useFileTree.js +59 -0
  28. package/dist/react/useFiles.d.ts +29 -0
  29. package/dist/react/useFiles.js +90 -0
  30. package/dist/react/useForeignKeyOptions.d.ts +18 -0
  31. package/dist/react/useFunctionRun.d.ts +17 -0
  32. package/dist/react/useJoinedRecords.d.ts +57 -2
  33. package/dist/react/useJoinedRecords.js +54 -5
  34. package/dist/react/useRecord.d.ts +16 -0
  35. package/dist/react/useRecordForm.d.ts +42 -3
  36. package/dist/react/useRecordForm.js +43 -6
  37. package/dist/react/useRecords.js +8 -5
  38. package/dist/react/useReferencingRecords.d.ts +66 -0
  39. package/dist/react/useReferencingRecords.js +159 -0
  40. package/dist/react/useRelatedRecords.d.ts +17 -0
  41. package/dist/react/useReverseRelatedRecords.d.ts +21 -0
  42. package/dist/react/useUpdateRecord.d.ts +34 -3
  43. package/dist/react/useUpdateRecord.js +13 -2
  44. package/dist/types.d.ts +6 -1
  45. package/package.json +2 -1
@@ -3,7 +3,7 @@ import { buildRecordFormValues, buildRecordPayload, } from "../record-form.js";
3
3
  import { normalizeError, resolvePodClient, stringifyComparable } from "./utils.js";
4
4
  import { useRecordSchema, } from "./useRecordSchema.js";
5
5
  const EMPTY_VALUES = {};
6
- 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, }) {
7
7
  const schema = useRecordSchema({
8
8
  client,
9
9
  podId,
@@ -11,6 +11,30 @@ export function useRecordForm({ client, podId, tableName, recordId = null, initi
11
11
  enabled,
12
12
  autoLoad,
13
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]);
14
38
  const [record, setRecord] = useState(null);
15
39
  const [values, setValuesState] = useState({});
16
40
  const [baselineValues, setBaselineValues] = useState({});
@@ -147,6 +171,19 @@ export function useRecordForm({ client, podId, tableName, recordId = null, initi
147
171
  setRecordError(null);
148
172
  try {
149
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
+ }
150
187
  const response = resolvedMode === "update" && recordId
151
188
  ? await scopedClient.records.update(tableName, recordId, payload.data)
152
189
  : await scopedClient.records.create(tableName, payload.data);
@@ -168,14 +205,14 @@ export function useRecordForm({ client, podId, tableName, recordId = null, initi
168
205
  finally {
169
206
  setIsSubmitting(false);
170
207
  }
171
- }, [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]);
172
209
  const isDirty = useMemo(() => {
173
210
  return stringifyComparable(values) !== stringifyComparable(baselineValues);
174
211
  }, [baselineValues, values]);
175
212
  return useMemo(() => ({
176
213
  table: schema.table,
177
- fields: schema.fields,
178
- editableFields: schema.editableFields,
214
+ fields: filteredFields,
215
+ editableFields: filteredEditableFields,
179
216
  defaults: schema.defaults,
180
217
  values,
181
218
  baselineValues,
@@ -197,6 +234,8 @@ export function useRecordForm({ client, podId, tableName, recordId = null, initi
197
234
  }), [
198
235
  baselineValues,
199
236
  fieldErrors,
237
+ filteredEditableFields,
238
+ filteredFields,
200
239
  isDirty,
201
240
  isLoadingRecord,
202
241
  isSubmitting,
@@ -206,9 +245,7 @@ export function useRecordForm({ client, podId, tableName, recordId = null, initi
206
245
  refreshRecord,
207
246
  reset,
208
247
  schema.defaults,
209
- schema.editableFields,
210
248
  schema.error,
211
- schema.fields,
212
249
  schema.isLoading,
213
250
  schema.refresh,
214
251
  schema.table,
@@ -72,7 +72,10 @@ export function useRecords({ client, podId, tableName, filters, sort, limit = 20
72
72
  trimmedTableName,
73
73
  ]);
74
74
  const loadMore = useCallback(async (overrides = {}) => {
75
- if (!isEnabled || !nextPageToken || isLoading || isLoadingMore) {
75
+ const loadedCount = records.length;
76
+ const canLoadWithCursor = Boolean(nextPageToken);
77
+ const canLoadWithOffset = loadedCount < total;
78
+ if (!isEnabled || isLoading || isLoadingMore || (!canLoadWithCursor && !canLoadWithOffset)) {
76
79
  return [];
77
80
  }
78
81
  setIsLoadingMore(true);
@@ -83,15 +86,15 @@ export function useRecords({ client, podId, tableName, filters, sort, limit = 20
83
86
  filters: stableFilters,
84
87
  sort: stableSort,
85
88
  limit: overrides.limit ?? limit,
86
- pageToken: nextPageToken,
87
- offset: overrides.offset,
89
+ pageToken: overrides.pageToken ?? nextPageToken ?? undefined,
90
+ offset: overrides.offset ?? (nextPageToken ? undefined : (offset ?? 0) + loadedCount),
88
91
  sortBy: overrides.sortBy ?? sortBy,
89
92
  order: overrides.order ?? order,
90
93
  params: overrides.params ?? stableParams,
91
94
  });
92
95
  const moreRecords = (response.items ?? []);
93
96
  setRecords((previous) => [...previous, ...moreRecords]);
94
- setTotal(response.total ?? records.length + moreRecords.length);
97
+ setTotal(response.total ?? loadedCount + moreRecords.length);
95
98
  setNextPageToken(response.next_page_token ?? null);
96
99
  return moreRecords;
97
100
  }
@@ -103,7 +106,7 @@ export function useRecords({ client, podId, tableName, filters, sort, limit = 20
103
106
  finally {
104
107
  setIsLoadingMore(false);
105
108
  }
106
- }, [client, isEnabled, isLoading, isLoadingMore, limit, nextPageToken, offset, order, podId, records.length, sortBy, stableFilters, stableParams, stableSort, trimmedTableName]);
109
+ }, [client, isEnabled, isLoading, isLoadingMore, limit, nextPageToken, offset, order, podId, records.length, sortBy, stableFilters, stableParams, stableSort, total, trimmedTableName]);
107
110
  useEffect(() => {
108
111
  if (!isEnabled) {
109
112
  setRecords([]);
@@ -0,0 +1,66 @@
1
+ import type { LemmaClient } from "../client.js";
2
+ import type { Table } from "../types.js";
3
+ /**
4
+ * React hook for fetching records from a referencing table that point back
5
+ * to a specific record via a foreign key.
6
+ *
7
+ * Unlike `useReverseRelatedRecords` which starts from a parent table and
8
+ * discovers what references it, this hook starts from the child table and
9
+ * says "give me all rows in this table where this FK column equals this ID."
10
+ *
11
+ * @example Fetch all comments for an issue
12
+ * ```tsx
13
+ * const { records, isLoading } = useReferencingRecords({
14
+ * client,
15
+ * table: "comments",
16
+ * foreignKey: "issue_id",
17
+ * recordId: "issue_123",
18
+ * });
19
+ * ```
20
+ *
21
+ * @example Fetch history entries
22
+ * ```tsx
23
+ * const { records, columns, isLoading } = useReferencingRecords({
24
+ * client,
25
+ * table: "issue_history",
26
+ * foreignKey: "issue_id",
27
+ * recordId: selectedIssueId,
28
+ * sortBy: "created_at",
29
+ * order: "desc",
30
+ * });
31
+ * ```
32
+ */
33
+ export interface ReferencingRecordsColumn {
34
+ key: string;
35
+ field: string;
36
+ label: string;
37
+ }
38
+ export interface UseReferencingRecordsOptions {
39
+ client: LemmaClient;
40
+ podId?: string;
41
+ /** The referencing (child) table, e.g. "comments". */
42
+ table: string;
43
+ /** The foreign-key column in the referencing table, e.g. "issue_id". */
44
+ foreignKey: string;
45
+ /** The record ID value to match against the foreign-key column. */
46
+ recordId?: string | null;
47
+ /** Fields to select. Auto-resolved from the table schema if omitted. */
48
+ fields?: string[];
49
+ limit?: number;
50
+ offset?: number;
51
+ sortBy?: string;
52
+ order?: "asc" | "desc" | string;
53
+ enabled?: boolean;
54
+ autoLoad?: boolean;
55
+ }
56
+ export interface UseReferencingRecordsResult<TRow extends Record<string, unknown> = Record<string, unknown>> {
57
+ referencedTable: Table | null;
58
+ columns: ReferencingRecordsColumn[];
59
+ records: TRow[];
60
+ total: number;
61
+ nextPageToken: string | null;
62
+ isLoading: boolean;
63
+ error: Error | null;
64
+ refresh: () => Promise<TRow[]>;
65
+ }
66
+ export declare function useReferencingRecords<TRow extends Record<string, unknown> = Record<string, unknown>>({ client, podId, table, foreignKey, recordId, fields, limit, offset, sortBy, order, enabled, autoLoad, }: UseReferencingRecordsOptions): UseReferencingRecordsResult<TRow>;
@@ -0,0 +1,159 @@
1
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
+ import { normalizeError, resolvePodClient, stringifyComparable } from "./utils.js";
3
+ function sentenceCase(value) {
4
+ return value
5
+ .replace(/[_\.]/g, " ")
6
+ .replace(/\s+/g, " ")
7
+ .trim()
8
+ .replace(/\b\w/g, (match) => match.toUpperCase());
9
+ }
10
+ function pickDefaultFields(table, foreignKey) {
11
+ const names = table.columns
12
+ .map((column) => column.name)
13
+ .filter((name) => name !== "created_at" && name !== "updated_at");
14
+ const prioritized = ["id", "name", "title", "label", "status", foreignKey];
15
+ const next = [];
16
+ prioritized.forEach((name) => {
17
+ if (names.includes(name) && !next.includes(name)) {
18
+ next.push(name);
19
+ }
20
+ });
21
+ names.forEach((name) => {
22
+ if (!next.includes(name)) {
23
+ next.push(name);
24
+ }
25
+ });
26
+ return next.slice(0, 6);
27
+ }
28
+ export function useReferencingRecords({ client, podId, table, foreignKey, recordId = null, fields, limit = 20, offset, sortBy, order, enabled = true, autoLoad = true, }) {
29
+ const [referencedTable, setReferencedTable] = useState(null);
30
+ const [columns, setColumns] = useState([]);
31
+ const [records, setRecords] = useState([]);
32
+ const [total, setTotal] = useState(0);
33
+ const [nextPageToken, setNextPageToken] = useState(null);
34
+ const [isLoading, setIsLoading] = useState(false);
35
+ const [error, setError] = useState(null);
36
+ const trimmedTable = table.trim();
37
+ const trimmedRecordId = typeof recordId === "string" ? recordId.trim() : "";
38
+ const fieldsKey = stringifyComparable(fields);
39
+ const stableFields = useMemo(() => fields, [fieldsKey]);
40
+ const isEnabled = enabled && trimmedTable.length > 0 && trimmedRecordId.length > 0;
41
+ const refresh = useCallback(async (signal) => {
42
+ if (!isEnabled) {
43
+ setReferencedTable(null);
44
+ setColumns([]);
45
+ setRecords([]);
46
+ setTotal(0);
47
+ setNextPageToken(null);
48
+ setError(null);
49
+ setIsLoading(false);
50
+ return [];
51
+ }
52
+ setIsLoading(true);
53
+ setError(null);
54
+ try {
55
+ const scopedClient = resolvePodClient(client, podId);
56
+ const tableResponse = await scopedClient.tables.get(trimmedTable);
57
+ if (signal?.aborted)
58
+ return [];
59
+ setReferencedTable(tableResponse);
60
+ const resolvedFields = (stableFields?.length ? stableFields : pickDefaultFields(tableResponse, foreignKey))
61
+ .filter((field, index, allFields) => field.trim().length > 0 && allFields.indexOf(field) === index);
62
+ const response = await scopedClient.records.list(trimmedTable, {
63
+ filters: [{
64
+ field: foreignKey,
65
+ op: "eq",
66
+ value: trimmedRecordId,
67
+ }],
68
+ limit,
69
+ offset,
70
+ sortBy,
71
+ order,
72
+ });
73
+ if (signal?.aborted)
74
+ return [];
75
+ const nextRecords = (response.items ?? []);
76
+ setColumns(resolvedFields.map((field) => ({
77
+ key: field,
78
+ field,
79
+ label: sentenceCase(field),
80
+ })));
81
+ setRecords(nextRecords);
82
+ setTotal(response.total ?? nextRecords.length);
83
+ setNextPageToken(response.next_page_token ?? null);
84
+ return nextRecords;
85
+ }
86
+ catch (refreshError) {
87
+ if (signal?.aborted)
88
+ return [];
89
+ const normalized = normalizeError(refreshError, "Failed to load referencing records.");
90
+ setError(normalized);
91
+ return [];
92
+ }
93
+ finally {
94
+ if (!signal?.aborted)
95
+ setIsLoading(false);
96
+ }
97
+ }, [
98
+ client,
99
+ foreignKey,
100
+ isEnabled,
101
+ limit,
102
+ offset,
103
+ order,
104
+ podId,
105
+ sortBy,
106
+ stableFields,
107
+ trimmedRecordId,
108
+ trimmedTable,
109
+ ]);
110
+ useEffect(() => {
111
+ if (!isEnabled) {
112
+ setReferencedTable(null);
113
+ setColumns([]);
114
+ setRecords([]);
115
+ setTotal(0);
116
+ setNextPageToken(null);
117
+ setError(null);
118
+ setIsLoading(false);
119
+ return;
120
+ }
121
+ if (!autoLoad)
122
+ return;
123
+ const controller = new AbortController();
124
+ let cancelled = false;
125
+ (async () => {
126
+ try {
127
+ await refresh(controller.signal);
128
+ }
129
+ catch {
130
+ if (!cancelled) {
131
+ setError(normalizeError(new Error("Failed to load referencing records."), "Failed to load referencing records."));
132
+ }
133
+ }
134
+ })();
135
+ return () => {
136
+ cancelled = true;
137
+ controller.abort();
138
+ };
139
+ }, [autoLoad, isEnabled, refresh]);
140
+ return useMemo(() => ({
141
+ referencedTable,
142
+ columns,
143
+ records,
144
+ total,
145
+ nextPageToken,
146
+ isLoading,
147
+ error,
148
+ refresh,
149
+ }), [
150
+ columns,
151
+ error,
152
+ isLoading,
153
+ nextPageToken,
154
+ records,
155
+ refresh,
156
+ referencedTable,
157
+ total,
158
+ ]);
159
+ }
@@ -1,5 +1,22 @@
1
1
  import type { LemmaClient } from "../client.js";
2
2
  import type { Table } from "../types.js";
3
+ /**
4
+ * React hook for fetching base-table rows with their FK-related data
5
+ * joined in a single query. You specify which FK columns to include and
6
+ * the hook auto-resolves the referenced table and join columns.
7
+ *
8
+ * The result has nested objects: `{ id, name, team: { id, name } }`.
9
+ *
10
+ * @example Issues with their team
11
+ * ```tsx
12
+ * const { records, isLoading } = useRelatedRecords({
13
+ * client,
14
+ * tableName: "issues",
15
+ * include: [{ foreignKey: "team_id" }],
16
+ * });
17
+ * // records[0] = { id: "1", title: "Bug", team: { id: "t1", name: "Eng" } }
18
+ * ```
19
+ */
3
20
  export interface RelatedRecordsInclude {
4
21
  foreignKey: string;
5
22
  as?: string;
@@ -1,5 +1,26 @@
1
1
  import type { LemmaClient } from "../client.js";
2
2
  import type { Table } from "../types.js";
3
+ /**
4
+ * React hook for finding records in *other* tables that reference a given
5
+ * record. Starts from the parent table, discovers all tables with FK
6
+ * columns pointing back to it, and fetches the referencing rows.
7
+ *
8
+ * For the simpler case where you already know the referencing table and
9
+ * FK column, prefer `useReferencingRecords` — it has a more intuitive API.
10
+ *
11
+ * @example All comments and history entries for an issue
12
+ * ```tsx
13
+ * const { relations, records, isLoading } = useReverseRelatedRecords({
14
+ * client,
15
+ * tableName: "issues",
16
+ * recordId: "issue_123",
17
+ * relation: { tableName: "comments", foreignKey: "issue_id" },
18
+ * });
19
+ * ```
20
+ *
21
+ * @see useReferencingRecords — flipped-perspective alias for the common
22
+ * "show me all rows in table X where FK = Y" pattern.
23
+ */
3
24
  export interface ReverseRelationSelector {
4
25
  tableName: string;
5
26
  foreignKey: string;
@@ -1,12 +1,43 @@
1
1
  import type { LemmaClient } from "../client.js";
2
- import type { RecordResponse } from "../types.js";
2
+ import type { FunctionRun, RecordResponse } from "../types.js";
3
+ /**
4
+ * React hook for updating a single record. Manages loading/error state and
5
+ * exposes an `update` function you can call from event handlers.
6
+ *
7
+ * Supports two modes:
8
+ * - `"direct"` (default): calls `records.update` directly.
9
+ * - `"function"`: calls `functions.runs.create`, routing the update through
10
+ * a pod function (e.g. for status transitions that log history).
11
+ *
12
+ * @example Direct update
13
+ * ```tsx
14
+ * const { update, isSubmitting } = useUpdateRecord({ client, tableName: "issues", recordId: "123" });
15
+ * await update({ status: "closed" });
16
+ * ```
17
+ *
18
+ * @example Function-backed update
19
+ * ```tsx
20
+ * const { update, isSubmitting } = useUpdateRecord({
21
+ * client,
22
+ * tableName: "issues",
23
+ * recordId: "123",
24
+ * updateVia: "function",
25
+ * updateFunctionName: "update-issue-status",
26
+ * });
27
+ * await update({ status: "in_progress" });
28
+ * ```
29
+ */
3
30
  export interface UseUpdateRecordOptions {
4
31
  client: LemmaClient;
5
32
  podId?: string;
6
33
  tableName: string;
7
34
  recordId?: string | null;
8
35
  enabled?: boolean;
9
- onSuccess?: (record: Record<string, unknown>, response: RecordResponse) => void;
36
+ /** How the record is updated. `"direct"` calls `records.update`. `"function"` calls `functions.runs.create`. */
37
+ updateVia?: "direct" | "function";
38
+ /** Function name to run when `updateVia` is `"function"`. Falls back to `tableName` if omitted. */
39
+ updateFunctionName?: string;
40
+ onSuccess?: (record: Record<string, unknown>, response: RecordResponse | FunctionRun) => void;
10
41
  onError?: (error: unknown) => void;
11
42
  }
12
43
  export interface UseUpdateRecordResult<TRecord extends Record<string, unknown> = Record<string, unknown>> {
@@ -18,4 +49,4 @@ export interface UseUpdateRecordResult<TRecord extends Record<string, unknown> =
18
49
  }) => Promise<TRecord | null>;
19
50
  reset: () => void;
20
51
  }
21
- export declare function useUpdateRecord<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, recordId, enabled, onSuccess, onError, }: UseUpdateRecordOptions): UseUpdateRecordResult<TRecord>;
52
+ export declare function useUpdateRecord<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, recordId, enabled, updateVia, updateFunctionName, onSuccess, onError, }: UseUpdateRecordOptions): UseUpdateRecordResult<TRecord>;
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { normalizeError, resolvePodClient } from "./utils.js";
3
- export function useUpdateRecord({ client, podId, tableName, recordId = null, enabled = true, onSuccess, onError, }) {
3
+ export function useUpdateRecord({ client, podId, tableName, recordId = null, enabled = true, updateVia = "direct", updateFunctionName, onSuccess, onError, }) {
4
4
  const [updatedRecord, setUpdatedRecord] = useState(null);
5
5
  const [isSubmitting, setIsSubmitting] = useState(false);
6
6
  const [error, setError] = useState(null);
@@ -22,6 +22,17 @@ export function useUpdateRecord({ client, podId, tableName, recordId = null, ena
22
22
  setError(null);
23
23
  try {
24
24
  const scopedClient = resolvePodClient(client, podId);
25
+ if (updateVia === "function") {
26
+ const functionName = updateFunctionName ?? trimmedTableName;
27
+ const input = { ...data, id: nextRecordId, record_id: nextRecordId };
28
+ const run = await scopedClient.functions.runs.create(functionName, { input });
29
+ const nextRecord = (run.output_data ?? { id: nextRecordId, ...data });
30
+ setUpdatedRecord(nextRecord);
31
+ if (nextRecord) {
32
+ onSuccessRef.current?.(nextRecord, run);
33
+ }
34
+ return nextRecord;
35
+ }
25
36
  const response = await scopedClient.records.update(trimmedTableName, nextRecordId, data);
26
37
  const nextRecord = (response.data ?? null);
27
38
  setUpdatedRecord(nextRecord);
@@ -39,7 +50,7 @@ export function useUpdateRecord({ client, podId, tableName, recordId = null, ena
39
50
  finally {
40
51
  setIsSubmitting(false);
41
52
  }
42
- }, [client, isEnabled, podId, trimmedRecordId, trimmedTableName]);
53
+ }, [client, isEnabled, podId, trimmedRecordId, trimmedTableName, updateFunctionName, updateVia]);
43
54
  const reset = useCallback(() => {
44
55
  setUpdatedRecord(null);
45
56
  setError(null);
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AgentResponse, AssistantResponse, AvailableModels, ColumnSchema, ConversationMessageResponse, ConversationResponse, CreateAgentRequest, CreateAssistantRequest, CreateTaskRequest, DatastoreQueryResponse, FlowRunEntity, FlowResponse, FunctionRunResponse, IconUploadResponse, OrganizationInvitationResponse, OrganizationMemberResponse, OrganizationResponse, PodConfigResponse, PodJoinRequestCreateResponse, PodMemberResponse, PodResponse, TableResponse, TaskMessageResponse, TaskResponse, UpdateAgentRequest, UpdateAssistantRequest, UserResponse } from "./openapi_client/index.js";
1
+ import type { AgentResponse, AssistantResponse, AvailableModels, ColumnSchema, ConversationMessageResponse, ConversationResponse, CreateAgentRequest, CreateAssistantRequest, CreateTaskRequest, DatastoreQueryResponse, DirectoryTreeNode, DirectoryTreeResponse, FileResponse, FileSearchResponse, FileSearchResultSchema, FlowRunEntity, FlowResponse, FunctionRunResponse, IconUploadResponse, OrganizationInvitationResponse, OrganizationMemberResponse, OrganizationResponse, PodConfigResponse, PodJoinRequestCreateResponse, PodMemberResponse, PodResponse, TableResponse, TaskMessageResponse, TaskResponse, UpdateAgentRequest, UpdateAssistantRequest, UserResponse } from "./openapi_client/index.js";
2
2
  /** Public ergonomic types. */
3
3
  export interface PageResult<T> {
4
4
  items: T[];
@@ -55,6 +55,11 @@ export type Workflow = FlowResponse;
55
55
  export type Table = TableResponse;
56
56
  export type TableColumn = ColumnSchema;
57
57
  export type DatastoreQueryResult = DatastoreQueryResponse;
58
+ export type DatastoreFile = FileResponse;
59
+ export type DatastoreFileSearchResponse = FileSearchResponse;
60
+ export type DatastoreFileSearchResult = FileSearchResultSchema;
61
+ export type DatastoreDirectoryTree = DirectoryTreeResponse;
62
+ export type DatastoreDirectoryTreeNode = DirectoryTreeNode;
58
63
  export type Pod = PodResponse;
59
64
  export type PodConfig = PodConfigResponse;
60
65
  export type PodMember = PodMemberResponse;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lemma-sdk",
3
- "version": "0.2.31",
3
+ "version": "0.2.33",
4
4
  "description": "Official TypeScript SDK for Lemma pod-scoped APIs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -45,6 +45,7 @@
45
45
  "build:bundle": "tsc -p tsconfig.bundle.json && node ./scripts/build_browser_bundle.mjs",
46
46
  "clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })\"",
47
47
  "registry:build": "shadcn build ./registry.json -o ./public/r",
48
+ "registry:check": "npm run registry:build && node ./scripts/check_registry_blocks.mjs",
48
49
  "prepublishOnly": "npm run build",
49
50
  "release:check": "npm run build && npm_config_cache=.npm-cache npm pack --dry-run"
50
51
  },