@objectstack/client-react 1.0.2 → 1.0.5

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.
@@ -1,398 +0,0 @@
1
- "use strict";
2
- /**
3
- * Data Query Hooks
4
- *
5
- * React hooks for querying and mutating ObjectStack data
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.useQuery = useQuery;
9
- exports.useMutation = useMutation;
10
- exports.usePagination = usePagination;
11
- exports.useInfiniteQuery = useInfiniteQuery;
12
- const react_1 = require("react");
13
- const context_1 = require("./context");
14
- /**
15
- * Hook for querying ObjectStack data with automatic caching and refetching
16
- *
17
- * @example
18
- * ```tsx
19
- * function TaskList() {
20
- * const { data, isLoading, error, refetch } = useQuery('todo_task', {
21
- * select: ['id', 'subject', 'priority'],
22
- * sort: ['-created_at'],
23
- * top: 20
24
- * });
25
- *
26
- * if (isLoading) return <div>Loading...</div>;
27
- * if (error) return <div>Error: {error.message}</div>;
28
- *
29
- * return (
30
- * <div>
31
- * {data?.value.map(task => (
32
- * <div key={task.id}>{task.subject}</div>
33
- * ))}
34
- * </div>
35
- * );
36
- * }
37
- * ```
38
- */
39
- function useQuery(object, options = {}) {
40
- const client = (0, context_1.useClient)();
41
- const [data, setData] = (0, react_1.useState)(null);
42
- const [isLoading, setIsLoading] = (0, react_1.useState)(true);
43
- const [isRefetching, setIsRefetching] = (0, react_1.useState)(false);
44
- const [error, setError] = (0, react_1.useState)(null);
45
- const intervalRef = (0, react_1.useRef)();
46
- const { query, select, filters, sort, top, skip, enabled = true, refetchInterval, onSuccess, onError } = options;
47
- const fetchData = (0, react_1.useCallback)(async (isRefetch = false) => {
48
- if (!enabled)
49
- return;
50
- try {
51
- if (isRefetch) {
52
- setIsRefetching(true);
53
- }
54
- else {
55
- setIsLoading(true);
56
- }
57
- setError(null);
58
- let result;
59
- if (query) {
60
- // Use advanced query API
61
- result = await client.data.query(object, query);
62
- }
63
- else {
64
- // Use simplified find API
65
- result = await client.data.find(object, {
66
- select,
67
- filters: filters,
68
- sort,
69
- top,
70
- skip
71
- });
72
- }
73
- setData(result);
74
- onSuccess?.(result);
75
- }
76
- catch (err) {
77
- const error = err instanceof Error ? err : new Error('Query failed');
78
- setError(error);
79
- onError?.(error);
80
- }
81
- finally {
82
- setIsLoading(false);
83
- setIsRefetching(false);
84
- }
85
- }, [client, object, query, select, filters, sort, top, skip, enabled, onSuccess, onError]);
86
- // Initial fetch and dependency-based refetch
87
- (0, react_1.useEffect)(() => {
88
- fetchData();
89
- }, [fetchData]);
90
- // Setup refetch interval
91
- (0, react_1.useEffect)(() => {
92
- if (refetchInterval && enabled) {
93
- intervalRef.current = setInterval(() => {
94
- fetchData(true);
95
- }, refetchInterval);
96
- return () => {
97
- if (intervalRef.current) {
98
- clearInterval(intervalRef.current);
99
- }
100
- };
101
- }
102
- return undefined;
103
- }, [refetchInterval, enabled, fetchData]);
104
- const refetch = (0, react_1.useCallback)(async () => {
105
- await fetchData(true);
106
- }, [fetchData]);
107
- return {
108
- data,
109
- isLoading,
110
- error,
111
- refetch,
112
- isRefetching
113
- };
114
- }
115
- /**
116
- * Hook for creating, updating, or deleting ObjectStack data
117
- *
118
- * @example
119
- * ```tsx
120
- * function CreateTaskForm() {
121
- * const { mutate, isLoading, error } = useMutation('todo_task', 'create', {
122
- * onSuccess: (data) => {
123
- * console.log('Task created:', data);
124
- * }
125
- * });
126
- *
127
- * const handleSubmit = (formData) => {
128
- * mutate(formData);
129
- * };
130
- *
131
- * return <form onSubmit={handleSubmit}>...</form>;
132
- * }
133
- * ```
134
- */
135
- function useMutation(object, operation, options = {}) {
136
- const client = (0, context_1.useClient)();
137
- const [data, setData] = (0, react_1.useState)(null);
138
- const [isLoading, setIsLoading] = (0, react_1.useState)(false);
139
- const [error, setError] = (0, react_1.useState)(null);
140
- const { onSuccess, onError, onSettled } = options;
141
- const mutateAsync = (0, react_1.useCallback)(async (variables) => {
142
- setIsLoading(true);
143
- setError(null);
144
- try {
145
- let result;
146
- switch (operation) {
147
- case 'create':
148
- result = await client.data.create(object, variables);
149
- break;
150
- case 'update':
151
- // Expect variables to be { id: string, data: Partial<T> }
152
- const updateVars = variables;
153
- result = await client.data.update(object, updateVars.id, updateVars.data);
154
- break;
155
- case 'delete':
156
- // Expect variables to be { id: string }
157
- const deleteVars = variables;
158
- result = await client.data.delete(object, deleteVars.id);
159
- break;
160
- case 'createMany':
161
- // createMany returns an array, which may not match TData type
162
- result = await client.data.createMany(object, variables);
163
- break;
164
- case 'updateMany':
165
- // Expect variables to be { records: Array<{ id: string, data: Partial<T> }> }
166
- const updateManyVars = variables;
167
- result = await client.data.updateMany(object, updateManyVars.records, updateManyVars.options);
168
- break;
169
- case 'deleteMany':
170
- // Expect variables to be { ids: string[] }
171
- const deleteManyVars = variables;
172
- result = await client.data.deleteMany(object, deleteManyVars.ids, deleteManyVars.options);
173
- break;
174
- default:
175
- throw new Error(`Unknown operation: ${operation}`);
176
- }
177
- setData(result);
178
- onSuccess?.(result, variables);
179
- onSettled?.(result, null, variables);
180
- return result;
181
- }
182
- catch (err) {
183
- const error = err instanceof Error ? err : new Error('Mutation failed');
184
- setError(error);
185
- onError?.(error, variables);
186
- onSettled?.(undefined, error, variables);
187
- throw error;
188
- }
189
- finally {
190
- setIsLoading(false);
191
- }
192
- }, [client, object, operation, onSuccess, onError, onSettled]);
193
- const mutate = (0, react_1.useCallback)((variables) => {
194
- return mutateAsync(variables).catch(() => {
195
- // Swallow error for non-async version
196
- // Error is still available in the error state
197
- return null;
198
- });
199
- }, [mutateAsync]);
200
- const reset = (0, react_1.useCallback)(() => {
201
- setData(null);
202
- setError(null);
203
- setIsLoading(false);
204
- }, []);
205
- return {
206
- mutate,
207
- mutateAsync,
208
- data,
209
- isLoading,
210
- error,
211
- reset
212
- };
213
- }
214
- /**
215
- * Hook for paginated data queries
216
- *
217
- * @example
218
- * ```tsx
219
- * function PaginatedTaskList() {
220
- * const {
221
- * data,
222
- * isLoading,
223
- * page,
224
- * totalPages,
225
- * nextPage,
226
- * previousPage,
227
- * hasNextPage,
228
- * hasPreviousPage
229
- * } = usePagination('todo_task', {
230
- * pageSize: 10,
231
- * sort: ['-created_at']
232
- * });
233
- *
234
- * return (
235
- * <div>
236
- * {data?.value.map(task => <div key={task.id}>{task.subject}</div>)}
237
- * <button onClick={previousPage} disabled={!hasPreviousPage}>Previous</button>
238
- * <span>Page {page} of {totalPages}</span>
239
- * <button onClick={nextPage} disabled={!hasNextPage}>Next</button>
240
- * </div>
241
- * );
242
- * }
243
- * ```
244
- */
245
- function usePagination(object, options = {}) {
246
- const { pageSize = 20, initialPage = 1, ...queryOptions } = options;
247
- const [page, setPage] = (0, react_1.useState)(initialPage);
248
- const queryResult = useQuery(object, {
249
- ...queryOptions,
250
- top: pageSize,
251
- skip: (page - 1) * pageSize
252
- });
253
- const totalCount = queryResult.data?.count || 0;
254
- const totalPages = Math.ceil(totalCount / pageSize);
255
- const hasNextPage = page < totalPages;
256
- const hasPreviousPage = page > 1;
257
- const nextPage = (0, react_1.useCallback)(() => {
258
- if (hasNextPage) {
259
- setPage(p => p + 1);
260
- }
261
- }, [hasNextPage]);
262
- const previousPage = (0, react_1.useCallback)(() => {
263
- if (hasPreviousPage) {
264
- setPage(p => p - 1);
265
- }
266
- }, [hasPreviousPage]);
267
- const goToPage = (0, react_1.useCallback)((newPage) => {
268
- const clampedPage = Math.max(1, Math.min(newPage, totalPages));
269
- setPage(clampedPage);
270
- }, [totalPages]);
271
- return {
272
- ...queryResult,
273
- page,
274
- totalPages,
275
- totalCount,
276
- nextPage,
277
- previousPage,
278
- goToPage,
279
- hasNextPage,
280
- hasPreviousPage
281
- };
282
- }
283
- /**
284
- * Hook for infinite scrolling / load more functionality
285
- *
286
- * @example
287
- * ```tsx
288
- * function InfiniteTaskList() {
289
- * const {
290
- * flatData,
291
- * isLoading,
292
- * fetchNextPage,
293
- * hasNextPage,
294
- * isFetchingNextPage
295
- * } = useInfiniteQuery('todo_task', {
296
- * pageSize: 20,
297
- * sort: ['-created_at']
298
- * });
299
- *
300
- * return (
301
- * <div>
302
- * {flatData.map(task => <div key={task.id}>{task.subject}</div>)}
303
- * {hasNextPage && (
304
- * <button onClick={fetchNextPage} disabled={isFetchingNextPage}>
305
- * {isFetchingNextPage ? 'Loading...' : 'Load More'}
306
- * </button>
307
- * )}
308
- * </div>
309
- * );
310
- * }
311
- * ```
312
- */
313
- function useInfiniteQuery(object, options = {}) {
314
- const client = (0, context_1.useClient)();
315
- const { pageSize = 20,
316
- // getNextPageParam is reserved for future use
317
- query, select, filters, sort, enabled = true, onSuccess, onError } = options;
318
- const [pages, setPages] = (0, react_1.useState)([]);
319
- const [isLoading, setIsLoading] = (0, react_1.useState)(true);
320
- const [isFetchingNextPage, setIsFetchingNextPage] = (0, react_1.useState)(false);
321
- const [error, setError] = (0, react_1.useState)(null);
322
- const [hasNextPage, setHasNextPage] = (0, react_1.useState)(true);
323
- const fetchPage = (0, react_1.useCallback)(async (skip, isNextPage = false) => {
324
- try {
325
- if (isNextPage) {
326
- setIsFetchingNextPage(true);
327
- }
328
- else {
329
- setIsLoading(true);
330
- }
331
- setError(null);
332
- let result;
333
- if (query) {
334
- result = await client.data.query(object, {
335
- ...query,
336
- limit: pageSize,
337
- offset: skip
338
- });
339
- }
340
- else {
341
- result = await client.data.find(object, {
342
- select,
343
- filters: filters,
344
- sort,
345
- top: pageSize,
346
- skip
347
- });
348
- }
349
- if (isNextPage) {
350
- setPages(prev => [...prev, result]);
351
- }
352
- else {
353
- setPages([result]);
354
- }
355
- // Determine if there's a next page
356
- const fetchedCount = result.value.length;
357
- const hasMore = fetchedCount === pageSize;
358
- setHasNextPage(hasMore);
359
- onSuccess?.(result);
360
- }
361
- catch (err) {
362
- const error = err instanceof Error ? err : new Error('Query failed');
363
- setError(error);
364
- onError?.(error);
365
- }
366
- finally {
367
- setIsLoading(false);
368
- setIsFetchingNextPage(false);
369
- }
370
- }, [client, object, query, select, filters, sort, pageSize, onSuccess, onError]);
371
- // Initial fetch
372
- (0, react_1.useEffect)(() => {
373
- if (enabled) {
374
- fetchPage(0);
375
- }
376
- }, [enabled, fetchPage]);
377
- const fetchNextPage = (0, react_1.useCallback)(async () => {
378
- if (!hasNextPage || isFetchingNextPage)
379
- return;
380
- const nextSkip = pages.length * pageSize;
381
- await fetchPage(nextSkip, true);
382
- }, [hasNextPage, isFetchingNextPage, pages.length, pageSize, fetchPage]);
383
- const refetch = (0, react_1.useCallback)(async () => {
384
- setPages([]);
385
- await fetchPage(0);
386
- }, [fetchPage]);
387
- const flatData = pages.flatMap(page => page.value);
388
- return {
389
- data: pages,
390
- flatData,
391
- isLoading,
392
- error,
393
- fetchNextPage,
394
- hasNextPage,
395
- isFetchingNextPage,
396
- refetch
397
- };
398
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,SAAS,EACT,KAAK,wBAAwB,EAC9B,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC5B,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,SAAS,EACT,OAAO,EACP,SAAS,EACT,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC"}
@@ -1,121 +0,0 @@
1
- /**
2
- * Metadata Hooks
3
- *
4
- * React hooks for accessing ObjectStack metadata (schemas, views, fields)
5
- */
6
- import { useClient } from './context';
7
- /**
8
- * Metadata query options
9
- */
10
- export interface UseMetadataOptions {
11
- /** Enable/disable automatic query execution */
12
- enabled?: boolean;
13
- /** Use cached metadata if available */
14
- useCache?: boolean;
15
- /** ETag for conditional requests */
16
- ifNoneMatch?: string;
17
- /** If-Modified-Since header for conditional requests */
18
- ifModifiedSince?: string;
19
- /** Callback on successful query */
20
- onSuccess?: (data: any) => void;
21
- /** Callback on error */
22
- onError?: (error: Error) => void;
23
- }
24
- /**
25
- * Metadata query result
26
- */
27
- export interface UseMetadataResult<T = any> {
28
- /** Metadata data */
29
- data: T | null;
30
- /** Loading state */
31
- isLoading: boolean;
32
- /** Error state */
33
- error: Error | null;
34
- /** Refetch the metadata */
35
- refetch: () => Promise<void>;
36
- /** ETag from last fetch */
37
- etag?: string;
38
- /** Whether data came from cache (304 Not Modified) */
39
- fromCache: boolean;
40
- }
41
- /**
42
- * Hook for fetching object schema/metadata
43
- *
44
- * @example
45
- * ```tsx
46
- * function ObjectSchemaViewer({ objectName }: { objectName: string }) {
47
- * const { data: schema, isLoading, error } = useObject(objectName);
48
- *
49
- * if (isLoading) return <div>Loading schema...</div>;
50
- * if (error) return <div>Error: {error.message}</div>;
51
- *
52
- * return (
53
- * <div>
54
- * <h2>{schema.label}</h2>
55
- * <p>Fields: {Object.keys(schema.fields).length}</p>
56
- * </div>
57
- * );
58
- * }
59
- * ```
60
- */
61
- export declare function useObject(objectName: string, options?: UseMetadataOptions): UseMetadataResult;
62
- /**
63
- * Hook for fetching view configuration
64
- *
65
- * @example
66
- * ```tsx
67
- * function ViewConfiguration({ objectName }: { objectName: string }) {
68
- * const { data: view, isLoading } = useView(objectName, 'list');
69
- *
70
- * if (isLoading) return <div>Loading view...</div>;
71
- *
72
- * return (
73
- * <div>
74
- * <h3>List View for {objectName}</h3>
75
- * <p>Columns: {view?.columns?.length}</p>
76
- * </div>
77
- * );
78
- * }
79
- * ```
80
- */
81
- export declare function useView(objectName: string, viewType?: 'list' | 'form', options?: UseMetadataOptions): UseMetadataResult;
82
- /**
83
- * Hook for extracting fields from object schema
84
- *
85
- * @example
86
- * ```tsx
87
- * function FieldList({ objectName }: { objectName: string }) {
88
- * const { data: fields, isLoading } = useFields(objectName);
89
- *
90
- * if (isLoading) return <div>Loading fields...</div>;
91
- *
92
- * return (
93
- * <ul>
94
- * {fields?.map(field => (
95
- * <li key={field.name}>{field.label} ({field.type})</li>
96
- * ))}
97
- * </ul>
98
- * );
99
- * }
100
- * ```
101
- */
102
- export declare function useFields(objectName: string, options?: UseMetadataOptions): UseMetadataResult<any[]>;
103
- /**
104
- * Generic metadata hook for custom metadata queries
105
- *
106
- * @example
107
- * ```tsx
108
- * function CustomMetadata() {
109
- * const { data, isLoading } = useMetadata(async (client) => {
110
- * // Custom metadata fetching logic
111
- * const object = await client.meta.getObject('custom_object');
112
- * const view = await client.meta.getView('custom_object', 'list');
113
- * return { object, view };
114
- * });
115
- *
116
- * return <pre>{JSON.stringify(data, null, 2)}</pre>;
117
- * }
118
- * ```
119
- */
120
- export declare function useMetadata<T = any>(fetcher: (client: ReturnType<typeof useClient>) => Promise<T>, options?: Omit<UseMetadataOptions, 'useCache' | 'ifNoneMatch' | 'ifModifiedSince'>): UseMetadataResult<T>;
121
- //# sourceMappingURL=metadata-hooks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"metadata-hooks.d.ts","sourceRoot":"","sources":["../src/metadata-hooks.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,+CAA+C;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAChC,wBAAwB;IACxB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,GAAG;IACxC,oBAAoB;IACpB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,oBAAoB;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,2BAA2B;IAC3B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CACvB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAyEnB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CACrB,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,MAAM,GAAG,MAAe,EAClC,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CA0CnB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CACvB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAAC,GAAG,EAAE,CAAC,CAc1B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,GAAG,EACjC,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAC7D,OAAO,GAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,aAAa,GAAG,iBAAiB,CAAM,GACrF,iBAAiB,CAAC,CAAC,CAAC,CA0CtB"}