@osdk/react 0.9.0-beta.5 → 0.9.0-beta.7

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 (72) hide show
  1. package/AGENTS.md +253 -0
  2. package/CHANGELOG.md +31 -0
  3. package/build/browser/intellisense.test.js +1 -1
  4. package/build/browser/intellisense.test.js.map +1 -1
  5. package/build/browser/new/platform-apis/admin/useCurrentFoundryUser.js +2 -2
  6. package/build/browser/new/platform-apis/admin/useCurrentFoundryUser.js.map +1 -1
  7. package/build/browser/new/platform-apis/admin/useFoundryUser.js +2 -2
  8. package/build/browser/new/platform-apis/admin/useFoundryUser.js.map +1 -1
  9. package/build/browser/new/platform-apis/admin/useFoundryUsersList.js +2 -2
  10. package/build/browser/new/platform-apis/admin/useFoundryUsersList.js.map +1 -1
  11. package/build/browser/new/useLinks.js +1 -1
  12. package/build/browser/new/useLinks.js.map +1 -1
  13. package/build/browser/new/useOsdkAggregation.js +1 -1
  14. package/build/browser/new/useOsdkAggregation.js.map +1 -1
  15. package/build/browser/new/useOsdkFunction.js +101 -0
  16. package/build/browser/new/useOsdkFunction.js.map +1 -0
  17. package/build/browser/new/useOsdkObject.js +1 -1
  18. package/build/browser/new/useOsdkObject.js.map +1 -1
  19. package/build/browser/new/useOsdkObjects.js +1 -1
  20. package/build/browser/new/useOsdkObjects.js.map +1 -1
  21. package/build/browser/public/experimental.js +1 -0
  22. package/build/browser/public/experimental.js.map +1 -1
  23. package/build/browser/utils/usePlatformQuery.js +1 -1
  24. package/build/browser/utils/usePlatformQuery.js.map +1 -1
  25. package/build/cjs/public/experimental.cjs +77 -379
  26. package/build/cjs/public/experimental.cjs.map +1 -1
  27. package/build/cjs/public/experimental.d.cts +139 -27
  28. package/build/esm/intellisense.test.js +1 -1
  29. package/build/esm/intellisense.test.js.map +1 -1
  30. package/build/esm/new/platform-apis/admin/useCurrentFoundryUser.js +2 -2
  31. package/build/esm/new/platform-apis/admin/useCurrentFoundryUser.js.map +1 -1
  32. package/build/esm/new/platform-apis/admin/useFoundryUser.js +2 -2
  33. package/build/esm/new/platform-apis/admin/useFoundryUser.js.map +1 -1
  34. package/build/esm/new/platform-apis/admin/useFoundryUsersList.js +2 -2
  35. package/build/esm/new/platform-apis/admin/useFoundryUsersList.js.map +1 -1
  36. package/build/esm/new/useLinks.js +1 -1
  37. package/build/esm/new/useLinks.js.map +1 -1
  38. package/build/esm/new/useOsdkAggregation.js +1 -1
  39. package/build/esm/new/useOsdkAggregation.js.map +1 -1
  40. package/build/esm/new/useOsdkFunction.js +101 -0
  41. package/build/esm/new/useOsdkFunction.js.map +1 -0
  42. package/build/esm/new/useOsdkObject.js +1 -1
  43. package/build/esm/new/useOsdkObject.js.map +1 -1
  44. package/build/esm/new/useOsdkObjects.js +1 -1
  45. package/build/esm/new/useOsdkObjects.js.map +1 -1
  46. package/build/esm/public/experimental.js +1 -0
  47. package/build/esm/public/experimental.js.map +1 -1
  48. package/build/esm/utils/usePlatformQuery.js +1 -1
  49. package/build/esm/utils/usePlatformQuery.js.map +1 -1
  50. package/build/types/new/platform-apis/admin/useCurrentFoundryUser.d.ts +2 -2
  51. package/build/types/new/platform-apis/admin/useCurrentFoundryUser.d.ts.map +1 -1
  52. package/build/types/new/platform-apis/admin/useFoundryUser.d.ts +4 -4
  53. package/build/types/new/platform-apis/admin/useFoundryUser.d.ts.map +1 -1
  54. package/build/types/new/platform-apis/admin/useFoundryUsersList.d.ts +4 -4
  55. package/build/types/new/platform-apis/admin/useFoundryUsersList.d.ts.map +1 -1
  56. package/build/types/new/useLinks.d.ts +5 -5
  57. package/build/types/new/useLinks.d.ts.map +1 -1
  58. package/build/types/new/useOsdkAggregation.d.ts +4 -5
  59. package/build/types/new/useOsdkAggregation.d.ts.map +1 -1
  60. package/build/types/new/useOsdkFunction.d.ts +112 -0
  61. package/build/types/new/useOsdkFunction.d.ts.map +1 -0
  62. package/build/types/new/useOsdkObjects.d.ts +5 -5
  63. package/build/types/new/useOsdkObjects.d.ts.map +1 -1
  64. package/build/types/public/experimental.d.ts +2 -0
  65. package/build/types/public/experimental.d.ts.map +1 -1
  66. package/docs/actions.md +414 -0
  67. package/docs/advanced-queries.md +657 -0
  68. package/docs/cache-management.md +213 -0
  69. package/docs/getting-started.md +382 -0
  70. package/docs/platform-apis.md +203 -0
  71. package/docs/querying-data.md +648 -0
  72. package/package.json +9 -6
@@ -0,0 +1,657 @@
1
+ ---
2
+ sidebar_position: 4
3
+ ---
4
+
5
+ # Advanced Queries
6
+
7
+ This guide covers advanced querying patterns including useObjectSet, derived properties, aggregations, and metadata.
8
+
9
+ ## useObjectSet
10
+
11
+ *Experimental - import from `@osdk/react/experimental`*
12
+
13
+ Advanced querying with set operations, derived properties, and link traversal.
14
+
15
+ ### When to Use useObjectSet vs useOsdkObjects
16
+
17
+ Both hooks support where, orderBy, pagination, withProperties, pivotTo, autoFetchMore, and streamUpdates.
18
+
19
+ **Use useOsdkObjects when:**
20
+ - Passing an ObjectType or Interface directly (`Todo`)
21
+
22
+ **Use useObjectSet when:**
23
+ - Starting from an ObjectSet instance (`$(Todo)`)
24
+ - Need set operations (`union`, `intersect`, `subtract`) with other ObjectSets
25
+
26
+ :::note
27
+ `useOsdkObjects` is in active development to reach full OSDK TypeScript client parity as it has more performance enhancements. `useObjectSet` currently supports everything and should be used if a feature you need isn't present in `useOsdkObjects`. Check the JSDoc for current feature support.
28
+ :::
29
+
30
+ ```tsx
31
+ import { $, Todo } from "@my/osdk";
32
+ import { useObjectSet, useOsdkObjects } from "@osdk/react/experimental";
33
+
34
+ // Simple query - use useOsdkObjects
35
+ const { data } = useOsdkObjects(Todo, {
36
+ where: { isComplete: false },
37
+ orderBy: { createdAt: "desc" },
38
+ });
39
+
40
+ // Set operations - use useObjectSet
41
+ const urgentTodos = $(Todo).where({ isUrgent: true });
42
+ const completedTodos = $(Todo).where({ isComplete: true });
43
+
44
+ const { data } = useObjectSet($(Todo), {
45
+ union: [urgentTodos],
46
+ subtract: [completedTodos],
47
+ });
48
+ ```
49
+
50
+ :::note The `$` function
51
+ The `$` function from your generated SDK creates an ObjectSet from an object type. `$(Todo)` creates an ObjectSet containing all Todo objects that you can then filter, union, intersect, or subtract with other ObjectSets.
52
+ :::
53
+
54
+ ### Basic Usage
55
+
56
+ ```tsx
57
+ import { $, Todo } from "@my/osdk";
58
+ import { useObjectSet } from "@osdk/react/experimental";
59
+
60
+ function TodosWithSetOperations() {
61
+ const allTodos = $(Todo);
62
+ const completedTodos = $(Todo).where({ isComplete: true });
63
+
64
+ const { data, isLoading, fetchMore } = useObjectSet(allTodos, {
65
+ subtract: [completedTodos],
66
+ where: { priority: "high" },
67
+ orderBy: { createdAt: "desc" },
68
+ pageSize: 20,
69
+ });
70
+
71
+ return (
72
+ <div>
73
+ {data?.map(todo => (
74
+ <div key={todo.$primaryKey}>
75
+ {todo.title}
76
+ </div>
77
+ ))}
78
+ </div>
79
+ );
80
+ }
81
+ ```
82
+
83
+ ### Set Operations
84
+
85
+ #### Union
86
+
87
+ Combine multiple object sets:
88
+
89
+ ```tsx
90
+ import { $, Todo } from "@my/osdk";
91
+ import { useObjectSet } from "@osdk/react/experimental";
92
+
93
+ function CombinedTodoQuery() {
94
+ const highPriorityTodos = $(Todo).where({ priority: "high" });
95
+ const urgentTodos = $(Todo).where({ isUrgent: true });
96
+
97
+ const { data } = useObjectSet(highPriorityTodos, {
98
+ union: [urgentTodos], // High priority OR urgent
99
+ });
100
+
101
+ return <div>High priority or urgent: {data?.length}</div>;
102
+ }
103
+ ```
104
+
105
+ #### Intersect
106
+
107
+ Find objects that exist in all sets:
108
+
109
+ ```tsx
110
+ import { $, Employee } from "@my/osdk";
111
+ import { useObjectSet } from "@osdk/react/experimental";
112
+
113
+ function SharedProjects({ employee1, employee2 }: {
114
+ employee1: Employee.OsdkInstance;
115
+ employee2: Employee.OsdkInstance;
116
+ }) {
117
+ const set1 = $(Employee).where({ id: employee1.id });
118
+ const set2 = $(Employee).where({ id: employee2.id });
119
+
120
+ const { data } = useObjectSet(set1, {
121
+ pivotTo: "projects",
122
+ intersect: [set2.$pivotTo("projects")],
123
+ });
124
+
125
+ return (
126
+ <div>
127
+ <h3>Shared Projects</h3>
128
+ {data?.map(project => (
129
+ <div key={project.$primaryKey}>{project.name}</div>
130
+ ))}
131
+ </div>
132
+ );
133
+ }
134
+ ```
135
+
136
+ #### Subtract
137
+
138
+ Remove objects that exist in another set:
139
+
140
+ ```tsx
141
+ import { $, Todo } from "@my/osdk";
142
+ import { useObjectSet } from "@osdk/react/experimental";
143
+
144
+ function ActiveTodos() {
145
+ const allTodos = $(Todo);
146
+ const completedTodos = $(Todo).where({ isComplete: true });
147
+
148
+ const { data } = useObjectSet(allTodos, {
149
+ subtract: [completedTodos],
150
+ });
151
+
152
+ return <div>Active todos: {data?.length}</div>;
153
+ }
154
+ ```
155
+
156
+ #### Combined Operations
157
+
158
+ ```tsx
159
+ import { $, Todo } from "@my/osdk";
160
+ import { useObjectSet } from "@osdk/react/experimental";
161
+
162
+ function ComplexTodoQuery() {
163
+ const highPriorityTodos = $(Todo).where({ priority: "high" });
164
+ const urgentTodos = $(Todo).where({ isUrgent: true });
165
+ const completedTodos = $(Todo).where({ isComplete: true });
166
+
167
+ const { data } = useObjectSet(highPriorityTodos, {
168
+ union: [urgentTodos], // High priority OR urgent
169
+ subtract: [completedTodos], // But not completed
170
+ });
171
+
172
+ return <div>High priority or urgent (but not completed): {data?.length}</div>;
173
+ }
174
+ ```
175
+
176
+ ### Link Traversal with pivotTo
177
+
178
+ Navigate to linked objects:
179
+
180
+ ```tsx
181
+ import { $, Employee } from "@my/osdk";
182
+ import { useObjectSet } from "@osdk/react/experimental";
183
+
184
+ function EmployeeDepartments({ employee }: { employee: Employee.OsdkInstance }) {
185
+ const employeeSet = $(Employee).where({ id: employee.id });
186
+
187
+ const { data } = useObjectSet(employeeSet, {
188
+ pivotTo: "department",
189
+ });
190
+
191
+ return (
192
+ <div>
193
+ Departments: {data?.map(dept => dept.name).join(", ")}
194
+ </div>
195
+ );
196
+ }
197
+ ```
198
+
199
+ ### Auto-Fetching and Streaming
200
+
201
+ ```tsx
202
+ import { $, Todo } from "@my/osdk";
203
+ import { useObjectSet } from "@osdk/react/experimental";
204
+
205
+ const { data, isLoading } = useObjectSet($(Todo), {
206
+ where: { isComplete: false },
207
+ autoFetchMore: 200, // Fetch at least 200 items
208
+ streamUpdates: true, // Real-time WebSocket updates
209
+ });
210
+ ```
211
+
212
+ ### All Options
213
+
214
+ - `where` - Filter objects
215
+ - `withProperties` - Add derived/computed properties
216
+ - `union` - Combine with other ObjectSets
217
+ - `intersect` - Find common objects with other ObjectSets
218
+ - `subtract` - Remove objects that exist in other ObjectSets
219
+ - `pivotTo` - Traverse to linked objects (changes result type)
220
+ - `pageSize` - Number of objects per page
221
+ - `orderBy` - Sort order
222
+ - `dedupeIntervalMs` - Minimum time between re-fetches (default: 2000ms)
223
+ - `streamUpdates` - Enable real-time websocket updates (default: false)
224
+ - `autoFetchMore` - Auto-fetch additional pages
225
+ - `enabled` - Enable/disable the query
226
+
227
+ ### Return Values
228
+
229
+ - `data` - Array of objects with derived properties
230
+ - `isLoading` - True while fetching
231
+ - `error` - Error object if fetch failed
232
+ - `fetchMore` - Function to load next page
233
+ - `objectSet` - The transformed ObjectSet after all operations
234
+
235
+ ---
236
+
237
+ ## Derived Properties
238
+
239
+ *Available in both useOsdkObjects and useObjectSet*
240
+
241
+ Add computed properties calculated server-side using the builder pattern.
242
+
243
+ ### Basic Usage
244
+
245
+ Derived properties use a builder function that receives a `DerivedProperty.Builder`:
246
+
247
+ ```tsx
248
+ import type { DerivedProperty } from "@osdk/client";
249
+ import { Employee } from "@my/osdk";
250
+ import { useOsdkObjects } from "@osdk/react/experimental";
251
+
252
+ const { data } = useOsdkObjects(Employee, {
253
+ where: { department: "Engineering" },
254
+ withProperties: {
255
+ // Get manager's name via link traversal
256
+ managerName: (base: DerivedProperty.Builder<Employee, false>) =>
257
+ base.pivotTo("manager").selectProperty("fullName"),
258
+
259
+ // Count direct reports
260
+ reportCount: (base: DerivedProperty.Builder<Employee, false>) =>
261
+ base.pivotTo("reports").aggregate("$count"),
262
+ },
263
+ });
264
+ ```
265
+
266
+ ### Builder Methods
267
+
268
+ The builder provides these methods:
269
+
270
+ - `.pivotTo(linkName)` - Navigate to linked objects
271
+ - `.selectProperty(propertyName)` - Select a property value
272
+ - `.aggregate(aggregation)` - Aggregate values (`"$count"`, `"propertyName:$avg"`, etc.)
273
+ - `.where(clause)` - Filter before aggregating
274
+
275
+ ### Advanced Examples
276
+
277
+ ```tsx
278
+ import type { DerivedProperty } from "@osdk/client";
279
+
280
+ const { data } = useOsdkObjects(Employee, {
281
+ where: { department: "Engineering" },
282
+ withProperties: {
283
+ // Chained traversal
284
+ departmentSize: (base: DerivedProperty.Builder<Employee, false>) =>
285
+ base.pivotTo("manager")
286
+ .pivotTo("reports")
287
+ .aggregate("$count"),
288
+
289
+ // Aggregate a specific property
290
+ avgReportSalary: (base: DerivedProperty.Builder<Employee, false>) =>
291
+ base.pivotTo("reports")
292
+ .selectProperty("salary")
293
+ .aggregate("$avg"),
294
+ },
295
+ });
296
+ ```
297
+
298
+ ### Filtering on Derived Properties
299
+
300
+ You can filter on derived properties in your where clause:
301
+
302
+ ```tsx
303
+ import type { DerivedProperty } from "@osdk/client";
304
+
305
+ const { data } = useOsdkObjects(Employee, {
306
+ withProperties: {
307
+ reportCount: (base: DerivedProperty.Builder<Employee, false>) =>
308
+ base.pivotTo("reports").aggregate("$count"),
309
+ },
310
+ where: {
311
+ department: "Engineering",
312
+ reportCount: { $gt: 0 }, // Only managers
313
+ },
314
+ });
315
+ ```
316
+
317
+ ---
318
+
319
+ ## useOsdkFunction
320
+
321
+ *Experimental - import from `@osdk/react/experimental`*
322
+
323
+ Execute and observe functions with request deduplication and configurable dependency tracking for automatic refetching.
324
+
325
+ ### Basic Usage
326
+
327
+ ```tsx
328
+ import { addOne } from "@my/osdk";
329
+ import { useOsdkFunction } from "@osdk/react/experimental";
330
+
331
+ function AddOneDemo() {
332
+ const { data, isLoading, error } = useOsdkFunction(addOne, {
333
+ params: { n: 5 },
334
+ });
335
+
336
+ if (isLoading && data === undefined) {
337
+ return <div>Calculating...</div>;
338
+ }
339
+
340
+ if (error) {
341
+ return <div>Error: {error.message}</div>;
342
+ }
343
+
344
+ return <div>Result: {data}</div>;
345
+ }
346
+ ```
347
+
348
+ ### Functions Without Parameters
349
+
350
+ ```tsx
351
+ import { getTodoCount } from "@my/osdk";
352
+ import { useOsdkFunction } from "@osdk/react/experimental";
353
+
354
+ function TodoCount() {
355
+ const { data, isLoading } = useOsdkFunction(getTodoCount);
356
+
357
+ return (
358
+ <div>
359
+ {isLoading && <span>Loading...</span>}
360
+ {data !== undefined && <span>Total todos: {data}</span>}
361
+ </div>
362
+ );
363
+ }
364
+ ```
365
+
366
+ ### Dependency Tracking
367
+
368
+ Automatically refetch when actions modify objects of specified types:
369
+
370
+ ```tsx
371
+ import { Employee, getEmployeeMetrics } from "@my/osdk";
372
+ import { useOsdkFunction } from "@osdk/react/experimental";
373
+
374
+ function EmployeeMetrics({ departmentId }: { departmentId: string }) {
375
+ const { data, isLoading, refetch } = useOsdkFunction(getEmployeeMetrics, {
376
+ params: { departmentId },
377
+ dependsOn: [Employee], // Refetch when any Employee changes
378
+ });
379
+
380
+ return (
381
+ <div>
382
+ {isLoading && <span>Updating...</span>}
383
+ {data && <span>Headcount: {data.headcount}</span>}
384
+ <button onClick={refetch}>Refresh</button>
385
+ </div>
386
+ );
387
+ }
388
+ ```
389
+
390
+ ### Specific Object Dependencies
391
+
392
+ For finer-grained control, depend on specific object instances:
393
+
394
+ ```tsx
395
+ import { Employee, getEmployeeReport } from "@my/osdk";
396
+ import { useOsdkFunction, useOsdkObject } from "@osdk/react/experimental";
397
+
398
+ function EmployeeReport({ employee }: { employee: Employee.OsdkInstance }) {
399
+ const { data, isLoading } = useOsdkFunction(getEmployeeReport, {
400
+ params: { employeeId: employee.$primaryKey },
401
+ dependsOnObjects: [employee], // Refetch only when this employee changes
402
+ });
403
+
404
+ return (
405
+ <div>
406
+ {isLoading && <span>Loading report...</span>}
407
+ {data && <pre>{JSON.stringify(data, null, 2)}</pre>}
408
+ </div>
409
+ );
410
+ }
411
+ ```
412
+
413
+ ### Conditional Execution
414
+
415
+ Use `enabled` to control when the function executes:
416
+
417
+ ```tsx
418
+ import { Employee, getEmployeeReport } from "@my/osdk";
419
+ import { useOsdkFunction, useOsdkObject } from "@osdk/react/experimental";
420
+
421
+ function ConditionalReport({ employeeId }: { employeeId: string }) {
422
+ const { object: employee } = useOsdkObject(Employee, employeeId);
423
+
424
+ const { data, isLoading } = useOsdkFunction(getEmployeeReport, {
425
+ params: { employeeId },
426
+ enabled: employee !== undefined, // Wait for employee to load
427
+ });
428
+
429
+ if (!employee) {
430
+ return <div>Loading employee...</div>;
431
+ }
432
+
433
+ return (
434
+ <div>
435
+ <h2>{employee.fullName}</h2>
436
+ {isLoading && <span>Loading report...</span>}
437
+ {data && <div>Report: {JSON.stringify(data)}</div>}
438
+ </div>
439
+ );
440
+ }
441
+ ```
442
+
443
+ ### Options
444
+
445
+ - `params` - Parameters to pass to the function (required if function has parameters)
446
+ - `dependsOn` - Array of object types; refetch when any object of these types changes
447
+ - `dependsOnObjects` - Array of specific object instances; refetch when these objects change
448
+ - `dedupeIntervalMs` - Milliseconds to dedupe identical calls (default: 2000)
449
+ - `enabled` - Enable/disable execution (default: true)
450
+
451
+ ### Return Values
452
+
453
+ - `data` - Function result, or undefined if not loaded or on error
454
+ - `isLoading` - True while the function is executing
455
+ - `error` - Error object if execution failed
456
+ - `lastUpdated` - Timestamp (ms since epoch) when result was last fetched
457
+ - `refetch` - Function to manually trigger a refetch
458
+
459
+ ---
460
+
461
+ ## useOsdkAggregation
462
+
463
+ *Experimental - import from `@osdk/react/experimental`*
464
+
465
+ Server-side grouping and aggregation.
466
+
467
+ ### Simple Aggregation
468
+
469
+ ```tsx
470
+ import { Todo } from "@my/osdk";
471
+ import { useOsdkAggregation } from "@osdk/react/experimental";
472
+
473
+ function TodoStats() {
474
+ const { data, isLoading, error } = useOsdkAggregation(Todo, {
475
+ aggregate: {
476
+ $select: {
477
+ totalCount: { $count: {} },
478
+ avgPriority: { $avg: "priority" },
479
+ maxDueDate: { $max: "dueDate" },
480
+ },
481
+ },
482
+ });
483
+
484
+ if (isLoading) {
485
+ return <div>Calculating stats...</div>;
486
+ }
487
+
488
+ if (error) {
489
+ return <div>Error: {JSON.stringify(error)}</div>;
490
+ }
491
+
492
+ return (
493
+ <div>
494
+ <p>Total Todos: {data?.totalCount}</p>
495
+ <p>Average Priority: {data?.avgPriority}</p>
496
+ <p>Latest Due Date: {data?.maxDueDate}</p>
497
+ </div>
498
+ );
499
+ }
500
+ ```
501
+
502
+ ### Grouped Aggregations
503
+
504
+ ```tsx
505
+ import { Todo } from "@my/osdk";
506
+ import { useOsdkAggregation } from "@osdk/react/experimental";
507
+
508
+ function TodosByStatus() {
509
+ const { data, isLoading } = useOsdkAggregation(Todo, {
510
+ aggregate: {
511
+ $groupBy: { status: "exact" },
512
+ $select: {
513
+ count: { $count: {} },
514
+ avgPriority: { $avg: "priority" },
515
+ },
516
+ },
517
+ });
518
+
519
+ if (isLoading) {
520
+ return <div>Loading...</div>;
521
+ }
522
+
523
+ return (
524
+ <div>
525
+ {data?.map((group, idx) => (
526
+ <div key={idx}>
527
+ <h3>Status: {group.$group.status}</h3>
528
+ <p>Count: {group.count}</p>
529
+ <p>Avg Priority: {group.avgPriority}</p>
530
+ </div>
531
+ ))}
532
+ </div>
533
+ );
534
+ }
535
+ ```
536
+
537
+ ### Filtered Aggregations
538
+
539
+ ```tsx
540
+ import { Todo } from "@my/osdk";
541
+ import { useOsdkAggregation } from "@osdk/react/experimental";
542
+
543
+ function HighPriorityStats() {
544
+ const { data, isLoading } = useOsdkAggregation(Todo, {
545
+ where: { priority: "high", isComplete: false },
546
+ aggregate: {
547
+ $select: {
548
+ count: { $count: {} },
549
+ earliestDue: { $min: "dueDate" },
550
+ },
551
+ },
552
+ });
553
+
554
+ if (isLoading || !data) return <div>Loading...</div>;
555
+
556
+ return (
557
+ <div>
558
+ <p>High Priority Incomplete: {data.count}</p>
559
+ <p>Earliest Due: {data.earliestDue}</p>
560
+ </div>
561
+ );
562
+ }
563
+ ```
564
+
565
+ ### Aggregation Operators
566
+
567
+ - `$count` - Count of objects: `{ $count: {} }`
568
+ - `$sum` - Sum of a property: `{ $sum: "propertyName" }`
569
+ - `$avg` - Average of a property: `{ $avg: "propertyName" }`
570
+ - `$min` - Minimum value: `{ $min: "propertyName" }`
571
+ - `$max` - Maximum value: `{ $max: "propertyName" }`
572
+
573
+ ### Options
574
+
575
+ - `where` - Filter objects before aggregation
576
+ - `withProperties` - Add derived properties for computed values
577
+ - `aggregate` - Aggregation configuration:
578
+ - `$select` (required) - Object mapping metric names to aggregation operators
579
+ - `$groupBy` (optional) - Object mapping property names to grouping strategy (e.g., `"exact"`, `{ $fixedWidth: 10 }`)
580
+ - `dedupeIntervalMs` - Minimum time between re-fetches (default: 2000ms)
581
+
582
+ ### Return Values
583
+
584
+ - `data` - Aggregation result (single object for non-grouped, array for grouped)
585
+ - `isLoading` - True while fetching
586
+ - `error` - Error object if fetch failed
587
+ - `refetch` - Manual refetch function
588
+
589
+ ---
590
+
591
+ ## useOsdkMetadata
592
+
593
+ *Stable - import from `@osdk/react`*
594
+
595
+ Fetch metadata about object types or interfaces.
596
+
597
+ ```tsx
598
+ import { Todo } from "@my/osdk";
599
+ import { useOsdkMetadata } from "@osdk/react";
600
+
601
+ function TodoMetadataViewer() {
602
+ const { metadata, loading, error } = useOsdkMetadata(Todo);
603
+
604
+ if (loading) {
605
+ return <div>Loading metadata...</div>;
606
+ }
607
+
608
+ if (error) {
609
+ return <div>Error: {error}</div>;
610
+ }
611
+
612
+ return (
613
+ <div>
614
+ <h2>{metadata?.displayName}</h2>
615
+ <p>Description: {metadata?.description}</p>
616
+ <h3>Properties:</h3>
617
+ <ul>
618
+ {Object.entries(metadata?.properties || {}).map(([key, prop]) => (
619
+ <li key={key}>
620
+ {key}: {prop.dataType.type}
621
+ {prop.displayName && ` (${prop.displayName})`}
622
+ </li>
623
+ ))}
624
+ </ul>
625
+ </div>
626
+ );
627
+ }
628
+ ```
629
+
630
+ ### Return Values
631
+
632
+ - `metadata` - ObjectMetadata or InterfaceMetadata with type information
633
+ - `loading` - True while fetching metadata
634
+ - `error` - Error message string if fetch failed
635
+
636
+ ---
637
+
638
+ ## Performance Considerations
639
+
640
+ ### useObjectSet
641
+
642
+ - Set operations (union, intersect, subtract) are performed on the server
643
+ - Each unique combination of options creates a separate cache entry
644
+ - Using `pivotTo` creates a new query for the linked type
645
+ - Consider using `pageSize` to limit initial data load
646
+
647
+ ### Derived Properties
648
+
649
+ - Computed server-side, so no client-side overhead
650
+ - Complex derived properties with many link traversals may be slower
651
+ - Filtering on derived properties happens server-side
652
+
653
+ ### Aggregations
654
+
655
+ - Aggregations are always computed server-side
656
+ - Use `where` to reduce the dataset before aggregation
657
+ - Group by produces array results that may be large