@powerhousedao/academy 3.3.0-dev.2 → 3.3.0-dev.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 3.3.0-dev.3 (2025-07-08)
2
+
3
+ ### 🚀 Features
4
+
5
+ - added operational hooks and utils in reactor-browser ([216f7d03d](https://github.com/powerhouse-inc/powerhouse/commit/216f7d03d))
6
+
7
+ ### ❤️ Thank You
8
+
9
+ - acaldas
10
+
1
11
  ## 3.3.0-dev.2 (2025-07-05)
2
12
 
3
13
  ### 🩹 Fixes
@@ -0,0 +1,798 @@
1
+ # Operational Database
2
+
3
+ This page covers the operational database tools available in Powerhouse applications, providing type-safe database operations with real-time updates through PGlite integration.
4
+
5
+ ## Overview
6
+
7
+ The operational database layer gives you powerful tools to work with data in your Powerhouse applications. You get type-safe queries, real-time updates, and a simple API that feels familiar to React developers.
8
+
9
+ **Key Benefits:**
10
+ - 🔒 **Type-safe queries** with full TypeScript support
11
+ - 🔄 **Live query capabilities** with real-time updates
12
+ - ⚡ **Automatic optimization** to prevent infinite re-renders
13
+ - 🎯 **Simple API** that abstracts away complexity
14
+ - 🧠 **Smart memoization** for parameters and queries
15
+
16
+ ## Quick Start
17
+
18
+ <details>
19
+ <summary>Setting up your first operational database query</summary>
20
+
21
+ ### Step 1: Define your database schema
22
+
23
+ ```typescript
24
+ type MyDatabase = {
25
+ users: {
26
+ id: number;
27
+ name: string;
28
+ email: string;
29
+ };
30
+ posts: {
31
+ id: number;
32
+ title: string;
33
+ content: string;
34
+ author_id: number;
35
+ };
36
+ };
37
+ ```
38
+
39
+ ### Step 2: Create a typed query hook
40
+
41
+ ```typescript
42
+ import { createTypedQuery } from '@powerhousedao/reactor-browser/operational';
43
+
44
+ const useTypedQuery = createTypedQuery<MyDatabase>();
45
+ ```
46
+
47
+ ### Step 3: Use it in your component
48
+
49
+ ```typescript
50
+ // Simple query - no parameters needed
51
+ export function useUserList() {
52
+ return useTypedQuery(db => {
53
+ return db.selectFrom('users').selectAll().compile();
54
+ });
55
+ }
56
+
57
+ // Query with parameters
58
+ export function useUserById(userId: number) {
59
+ return useTypedQuery(
60
+ (db, params) => {
61
+ return db
62
+ .selectFrom('users')
63
+ .selectAll()
64
+ .where('id', '=', params.userId)
65
+ .compile();
66
+ },
67
+ { userId }
68
+ );
69
+ }
70
+ ```
71
+
72
+ ### Step 4: Use in your React component
73
+
74
+ ```typescript
75
+ function UserList() {
76
+ const { isLoading, error, result } = useUserList();
77
+
78
+ if (isLoading) return <div>Loading...</div>;
79
+ if (error) return <div>Error: {error.message}</div>;
80
+ if (!result) return <div>No data</div>;
81
+
82
+ return (
83
+ <ul>
84
+ {result.rows.map(user => (
85
+ <li key={user.id}>
86
+ {user.name} - {user.email}
87
+ </li>
88
+ ))}
89
+ </ul>
90
+ );
91
+ }
92
+ ```
93
+
94
+ </details>
95
+
96
+ ## Core Hooks
97
+
98
+ ### 1. createTypedQuery()
99
+
100
+ <details>
101
+ <summary>`createTypedQuery<Schema>()`: Creates a typed query hook for your database schema</summary>
102
+
103
+ ### Hook Name and Signature
104
+
105
+ ```typescript
106
+ function createTypedQuery<Schema>(): TypedQueryHook<Schema>
107
+ ```
108
+
109
+ ### Description
110
+
111
+ Creates a typed query hook that provides type-safe database operations with live query capabilities. This is the main hook you'll use for most operational database operations in your components.
112
+
113
+ ### Usage Example
114
+
115
+ ```typescript
116
+ import { createTypedQuery } from '@powerhousedao/reactor-browser/operational';
117
+
118
+ type AppDatabase = {
119
+ users: { id: number; name: string; email: string };
120
+ posts: { id: number; title: string; author_id: number };
121
+ };
122
+
123
+ const useTypedQuery = createTypedQuery<AppDatabase>();
124
+
125
+ // Static query (no parameters)
126
+ function useAllUsers() {
127
+ return useTypedQuery(db => {
128
+ return db.selectFrom('users').selectAll().compile();
129
+ });
130
+ }
131
+
132
+ // Dynamic query with parameters
133
+ function useUsersByStatus(status: string) {
134
+ return useTypedQuery(
135
+ (db, params) => {
136
+ return db
137
+ .selectFrom('users')
138
+ .selectAll()
139
+ .where('status', '=', params.status)
140
+ .compile();
141
+ },
142
+ { status }
143
+ );
144
+ }
145
+ ```
146
+
147
+ ### Parameters
148
+
149
+ The returned hook has two overloads:
150
+
151
+ **Static queries (no parameters):**
152
+ - `queryCallback: (db: EnhancedKysely<Schema>) => QueryCallbackReturnType` - Function that receives the database instance and returns a query
153
+
154
+ **Parameterized queries:**
155
+ - `queryCallback: (db: EnhancedKysely<Schema>, parameters: TParams) => QueryCallbackReturnType` - Function that receives the database instance and parameters
156
+ - `parameters: TParams` - Parameters for the query (automatically memoized)
157
+
158
+ ### Return Value
159
+
160
+ ```typescript
161
+ {
162
+ isLoading: boolean; // True while query is loading
163
+ error: Error | null; // Any error that occurred
164
+ result: LiveQueryResults<T> | null; // Query results with real-time updates
165
+ }
166
+ ```
167
+
168
+ ### Notes / Caveats
169
+
170
+ - Parameters are automatically memoized using deep comparison
171
+ - Queries update in real-time when the database changes
172
+ - The callback must return an object with `sql` and optional `parameters` properties
173
+ - Use `.compile()` on Kysely queries to get the required format
174
+
175
+ ### Related Hooks
176
+
177
+ - [`useOperationalStore`](#useoperationalstore) - For direct database access
178
+ - [`useOperationalQuery`](#useoperationalquery) - Lower-level query hook
179
+
180
+ </details>
181
+
182
+ ### 2. useOperationalStore()
183
+
184
+ <details>
185
+ <summary>`useOperationalStore<Schema>()`: Access the enhanced database instance directly</summary>
186
+
187
+ ### Hook Name and Signature
188
+
189
+ ```typescript
190
+ function useOperationalStore<Schema>(): IOperationalStore<Schema>
191
+ ```
192
+
193
+ ### Description
194
+
195
+ Provides direct access to the enhanced Kysely database instance with live query capabilities. Use this when you need to perform operational database operations outside of the typical query patterns.
196
+
197
+ ### Usage Example
198
+
199
+ ```typescript
200
+ import { useOperationalStore } from '@powerhousedao/reactor-browser/operational';
201
+
202
+ function DatabaseOperations() {
203
+ const { db, isLoading, error } = useOperationalStore<MyDatabase>();
204
+
205
+ const createUser = async (name: string, email: string) => {
206
+ if (!db) return;
207
+
208
+ // Direct database operations
209
+ await db
210
+ .insertInto('users')
211
+ .values({ name, email })
212
+ .execute();
213
+ };
214
+
215
+ if (isLoading) return <div>Database initializing...</div>;
216
+ if (error) return <div>Database error: {error.message}</div>;
217
+
218
+ return (
219
+ <button onClick={() => createUser('John', 'john@example.com')}>
220
+ Create User
221
+ </button>
222
+ );
223
+ }
224
+ ```
225
+
226
+ ### Parameters
227
+
228
+ - `Schema` - TypeScript type defining your database schema
229
+
230
+ ### Return Value
231
+
232
+ ```typescript
233
+ {
234
+ db: EnhancedKysely<Schema> | null; // Enhanced Kysely instance with live capabilities
235
+ isLoading: boolean; // True while database is initializing
236
+ error: Error | null; // Any initialization error
237
+ }
238
+ ```
239
+
240
+ ### Notes / Caveats
241
+
242
+ - Always check if `db` is not null before using it
243
+ - The database instance includes both Kysely methods and live query capabilities
244
+ - Use this for direct database operations like inserts, updates, and deletes
245
+ - For queries, prefer `createTypedQuery()` which provides better optimization
246
+
247
+ ### Related Hooks
248
+
249
+ - [`createTypedQuery`](#createtypedquery) - For optimized queries
250
+ - [`useOperationalQuery`](#useoperationalquery) - For manual query control
251
+
252
+ </details>
253
+
254
+ ### 3. useOperationalQuery()
255
+
256
+ <details>
257
+ <summary>`useOperationalQuery<Schema, T, TParams>()`: Lower-level hook for manual query control</summary>
258
+
259
+ ### Hook Name and Signature
260
+
261
+ ```typescript
262
+ function useOperationalQuery<Schema, T, TParams>(
263
+ queryCallback: (db: EnhancedKysely<Schema>, parameters?: TParams) => QueryCallbackReturnType,
264
+ parameters?: TParams
265
+ ): QueryResult<T>
266
+ ```
267
+
268
+ ### Description
269
+
270
+ Lower-level hook for creating live queries with manual control over the query callback and parameters. Most developers should use `createTypedQuery()` instead, but this hook is useful for advanced use cases.
271
+
272
+ ### Usage Example
273
+
274
+ ```typescript
275
+ import { useOperationalQuery } from '@powerhousedao/reactor-browser/operational';
276
+
277
+ function UserCount() {
278
+ const { result, isLoading, error } = useOperationalQuery<MyDatabase, { count: number }>(
279
+ (db) => {
280
+ return db
281
+ .selectFrom('users')
282
+ .select(db.fn.count('id').as('count'))
283
+ .compile();
284
+ }
285
+ );
286
+
287
+ if (isLoading) return <div>Loading...</div>;
288
+ if (error) return <div>Error: {error.message}</div>;
289
+
290
+ return <div>User count: {result?.rows[0]?.count ?? 0}</div>;
291
+ }
292
+ ```
293
+
294
+ ### Parameters
295
+
296
+ - `queryCallback` - Function that receives the database instance and optional parameters
297
+ - `parameters` - Optional parameters for the query
298
+
299
+ ### Return Value
300
+
301
+ ```typescript
302
+ {
303
+ result: LiveQueryResults<T> | null; // Live query results
304
+ isLoading: boolean; // Combined loading state
305
+ error: Error | null; // Any error that occurred
306
+ }
307
+ ```
308
+
309
+ ### Notes / Caveats
310
+
311
+ - This hook doesn't include automatic parameter memoization
312
+ - Use `createTypedQuery()` for better developer experience and optimization
313
+ - Useful for cases where you need manual control over the query lifecycle
314
+
315
+ ### Related Hooks
316
+
317
+ - [`createTypedQuery`](#createtypedquery) - Recommended higher-level API
318
+ - [`useOperationalStore`](#useoperationalstore) - For direct database access
319
+
320
+ </details>
321
+
322
+ ## Advanced Patterns
323
+
324
+ ### Working with Dynamic Parameters
325
+
326
+ <details>
327
+ <summary>How to handle parameters that change over time</summary>
328
+
329
+ ### Problem
330
+
331
+ You need to create queries that update automatically when search terms, filters, or other parameters change.
332
+
333
+ ### Solution
334
+
335
+ The `createTypedQuery` hook automatically handles parameter changes and memoizes them using deep comparison:
336
+
337
+ ```typescript
338
+ function useSearchResults() {
339
+ const [searchTerm, setSearchTerm] = useState('');
340
+ const [category, setCategory] = useState('all');
341
+
342
+ // Query automatically updates when searchTerm or category changes
343
+ const result = useTypedQuery(
344
+ (db, params) => {
345
+ let query = db.selectFrom('products').selectAll();
346
+
347
+ if (params.searchTerm) {
348
+ query = query.where('name', 'like', `%${params.searchTerm}%`);
349
+ }
350
+
351
+ if (params.category !== 'all') {
352
+ query = query.where('category', '=', params.category);
353
+ }
354
+
355
+ return query.compile();
356
+ },
357
+ { searchTerm, category }
358
+ );
359
+
360
+ return { result, setSearchTerm, setCategory };
361
+ }
362
+ ```
363
+
364
+ ### Key Points
365
+
366
+ - Parameters are automatically memoized using deep comparison
367
+ - No need to wrap parameters in `useMemo`
368
+ - Query re-runs only when parameter values actually change
369
+ - Works with complex nested objects
370
+
371
+ </details>
372
+
373
+ ### Custom SQL Queries
374
+
375
+ <details>
376
+ <summary>Using raw SQL instead of Kysely query builder</summary>
377
+
378
+ ### Problem
379
+
380
+ You need to write complex SQL queries that are easier to express in raw SQL than using the Kysely query builder.
381
+
382
+ ### Solution
383
+
384
+ You can return raw SQL queries from your callback:
385
+
386
+ ```typescript
387
+ function useCustomUserStats() {
388
+ return useTypedQuery(() => {
389
+ return {
390
+ sql: `
391
+ SELECT
392
+ u.name,
393
+ COUNT(p.id) as post_count,
394
+ MAX(p.created_at) as last_post_date
395
+ FROM users u
396
+ LEFT JOIN posts p ON u.id = p.author_id
397
+ GROUP BY u.id, u.name
398
+ ORDER BY post_count DESC
399
+ `
400
+ };
401
+ });
402
+ }
403
+
404
+ // With parameters
405
+ function useUserPostsByDateRange(startDate: string, endDate: string) {
406
+ return useTypedQuery(
407
+ (db, params) => {
408
+ return {
409
+ sql: `
410
+ SELECT p.*, u.name as author_name
411
+ FROM posts p
412
+ JOIN users u ON p.author_id = u.id
413
+ WHERE p.created_at BETWEEN $1 AND $2
414
+ ORDER BY p.created_at DESC
415
+ `,
416
+ parameters: [params.startDate, params.endDate]
417
+ };
418
+ },
419
+ { startDate, endDate }
420
+ );
421
+ }
422
+ ```
423
+
424
+ ### Key Points
425
+
426
+ - Return an object with `sql` and optional `parameters` properties
427
+ - Use parameterized queries ($1, $2, etc.) for dynamic values
428
+ - You can mix Kysely and raw SQL approaches in the same application
429
+
430
+ </details>
431
+
432
+ ### Complex Joins and Relationships
433
+
434
+ <details>
435
+ <summary>Working with related data across multiple tables</summary>
436
+
437
+ ### Problem
438
+
439
+ You need to fetch related data from multiple tables with complex relationships.
440
+
441
+ ### Solution
442
+
443
+ Use Kysely's join capabilities within your query callbacks:
444
+
445
+ ```typescript
446
+ function useUsersWithPosts() {
447
+ return useTypedQuery(db => {
448
+ return db
449
+ .selectFrom('users')
450
+ .leftJoin('posts', 'users.id', 'posts.author_id')
451
+ .select([
452
+ 'users.id',
453
+ 'users.name',
454
+ 'users.email',
455
+ 'posts.title as post_title',
456
+ 'posts.content as post_content'
457
+ ])
458
+ .compile();
459
+ });
460
+ }
461
+
462
+ // More complex example with multiple joins and aggregations
463
+ function useUserDashboardData(userId: number) {
464
+ return useTypedQuery(
465
+ (db, params) => {
466
+ return db
467
+ .selectFrom('users')
468
+ .leftJoin('posts', 'users.id', 'posts.author_id')
469
+ .leftJoin('comments', 'posts.id', 'comments.post_id')
470
+ .select([
471
+ 'users.id',
472
+ 'users.name',
473
+ 'users.email',
474
+ db.fn.count('posts.id').as('post_count'),
475
+ db.fn.count('comments.id').as('comment_count')
476
+ ])
477
+ .where('users.id', '=', params.userId)
478
+ .groupBy(['users.id', 'users.name', 'users.email'])
479
+ .compile();
480
+ },
481
+ { userId }
482
+ );
483
+ }
484
+ ```
485
+
486
+ ### Key Points
487
+
488
+ - Use Kysely's join methods for related data
489
+ - Leverage aggregation functions for counts and calculations
490
+ - Type safety is maintained throughout complex queries
491
+
492
+ </details>
493
+
494
+ ## Best Practices
495
+
496
+ ### 1. Schema Definition
497
+
498
+ <details>
499
+ <summary>How to properly define your database schema types</summary>
500
+
501
+ Always define clear TypeScript interfaces for your database schema:
502
+
503
+ ```typescript
504
+ // ✅ Good - Clear, typed schema
505
+ type AppDatabase = {
506
+ users: {
507
+ id: number;
508
+ name: string;
509
+ email: string;
510
+ created_at: Date;
511
+ updated_at: Date;
512
+ };
513
+ posts: {
514
+ id: number;
515
+ title: string;
516
+ content: string;
517
+ author_id: number;
518
+ published: boolean;
519
+ created_at: Date;
520
+ };
521
+ };
522
+
523
+ // ❌ Avoid - Vague or missing types
524
+ type BadDatabase = {
525
+ users: any;
526
+ posts: Record<string, unknown>;
527
+ };
528
+ ```
529
+
530
+ </details>
531
+
532
+ ### 2. Hook Organization
533
+
534
+ <details>
535
+ <summary>How to organize your database hooks</summary>
536
+
537
+ Create focused, reusable hooks for different data access patterns:
538
+
539
+ ```typescript
540
+ // ✅ Good - Focused, reusable hooks
541
+ export function useUsers() {
542
+ return useTypedQuery(db =>
543
+ db.selectFrom('users').selectAll().compile()
544
+ );
545
+ }
546
+
547
+ export function useUserById(id: number) {
548
+ return useTypedQuery(
549
+ (db, params) => db
550
+ .selectFrom('users')
551
+ .selectAll()
552
+ .where('id', '=', params.id)
553
+ .compile(),
554
+ { id }
555
+ );
556
+ }
557
+
558
+ export function useActiveUsers() {
559
+ return useTypedQuery(db =>
560
+ db.selectFrom('users')
561
+ .selectAll()
562
+ .where('active', '=', true)
563
+ .compile()
564
+ );
565
+ }
566
+
567
+ // ❌ Avoid - Too generic or complex
568
+ export function useEverything() {
569
+ return useTypedQuery(db =>
570
+ db.selectFrom('users')
571
+ .leftJoin('posts', 'users.id', 'posts.author_id')
572
+ .leftJoin('comments', 'posts.id', 'comments.post_id')
573
+ .selectAll() // Too much data
574
+ .compile()
575
+ );
576
+ }
577
+ ```
578
+
579
+ </details>
580
+
581
+ ### 3. Error Handling
582
+
583
+ <details>
584
+ <summary>How to handle loading states and errors</summary>
585
+
586
+ Always handle loading and error states in your components:
587
+
588
+ ```typescript
589
+ function UserList() {
590
+ const { isLoading, error, result } = useUsers();
591
+
592
+ // ✅ Good - Handle all states
593
+ if (isLoading) return <LoadingSpinner />;
594
+ if (error) return <ErrorMessage error={error} />;
595
+ if (!result) return <NoDataMessage />;
596
+
597
+ return (
598
+ <ul>
599
+ {result.rows.map(user => (
600
+ <li key={user.id}>{user.name}</li>
601
+ ))}
602
+ </ul>
603
+ );
604
+ }
605
+
606
+ // ❌ Avoid - Missing error handling
607
+ function BadUserList() {
608
+ const { result } = useUsers();
609
+
610
+ return (
611
+ <ul>
612
+ {result?.rows.map(user => (
613
+ <li key={user.id}>{user.name}</li>
614
+ ))}
615
+ </ul>
616
+ );
617
+ }
618
+ ```
619
+
620
+ </details>
621
+
622
+ ### 4. Performance Optimization
623
+
624
+ <details>
625
+ <summary>Tips for optimal query performance</summary>
626
+
627
+ - **Keep queries focused**: Don't select unnecessary columns or join too many tables
628
+ - **Use parameters wisely**: The automatic memoization handles most cases, but avoid creating new objects unnecessarily
629
+ - **Consider query frequency**: For data that changes rarely, consider caching strategies
630
+
631
+ ```typescript
632
+ // ✅ Good - Focused query
633
+ function useUserNames() {
634
+ return useTypedQuery(db =>
635
+ db.selectFrom('users')
636
+ .select(['id', 'name']) // Only what you need
637
+ .compile()
638
+ );
639
+ }
640
+
641
+ // ✅ Good - Stable parameters
642
+ function useUsersByStatus(status: string) {
643
+ return useTypedQuery(
644
+ (db, params) => db
645
+ .selectFrom('users')
646
+ .selectAll()
647
+ .where('status', '=', params.status)
648
+ .compile(),
649
+ { status } // Simple, stable parameter
650
+ );
651
+ }
652
+
653
+ // ❌ Avoid - Unnecessary data
654
+ function useEverythingAboutUsers() {
655
+ return useTypedQuery(db =>
656
+ db.selectFrom('users')
657
+ .leftJoin('posts', 'users.id', 'posts.author_id')
658
+ .selectAll() // Too much data
659
+ .compile()
660
+ );
661
+ }
662
+ ```
663
+
664
+ </details>
665
+
666
+ ## Common Issues and Solutions
667
+
668
+ ### Query Not Updating
669
+
670
+ <details>
671
+ <summary>My query results aren't updating when I expect them to</summary>
672
+
673
+ ### Problem
674
+ Your query results don't update when you expect them to, even though you've changed parameters.
675
+
676
+ ### Solution
677
+ Check that your parameters are actually changing in content, not just reference:
678
+
679
+ ```typescript
680
+ // ✅ Good - Parameters change in content
681
+ const [userId, setUserId] = useState(1);
682
+ const result = useUserById(userId); // Updates when userId changes
683
+
684
+ // ❌ Common mistake - Same content, different objects
685
+ const result = useTypedQuery(
686
+ (db, params) => /* query */,
687
+ { userId: user.id } // New object every render, but same content
688
+ );
689
+
690
+ // ✅ Better - Extract stable values
691
+ const userId = user.id;
692
+ const result = useTypedQuery(
693
+ (db, params) => /* query */,
694
+ { userId } // Stable parameter
695
+ );
696
+ ```
697
+
698
+ ### Debugging Tips
699
+ - Log your parameters to see if they're actually changing
700
+ - Check the `isLoading` state to see if queries are re-running
701
+ - Use React DevTools to inspect hook state changes
702
+
703
+ </details>
704
+
705
+ ### Type Errors
706
+
707
+ <details>
708
+ <summary>Getting TypeScript errors with my queries</summary>
709
+
710
+ ### Problem
711
+ TypeScript is showing errors about query return types or database schema.
712
+
713
+ ### Solution
714
+ Make sure your callback returns the correct type:
715
+
716
+ ```typescript
717
+ // ✅ Good - Returns QueryCallbackReturnType
718
+ const result = useTypedQuery(db => {
719
+ return db.selectFrom('users').selectAll().compile(); // Has sql property
720
+ });
721
+
722
+ // ❌ Error - Missing .compile()
723
+ const result = useTypedQuery(db => {
724
+ return db.selectFrom('users').selectAll(); // No sql property
725
+ });
726
+
727
+ // ✅ Good - Raw SQL format
728
+ const result = useTypedQuery(() => {
729
+ return {
730
+ sql: 'SELECT * FROM users',
731
+ parameters: []
732
+ };
733
+ });
734
+ ```
735
+
736
+ </details>
737
+
738
+ ### Performance Issues
739
+
740
+ <details>
741
+ <summary>My queries are running too frequently or causing lag</summary>
742
+
743
+ ### Problem
744
+ Your queries are running more often than expected, causing performance issues.
745
+
746
+ ### Solution
747
+ Check for unstable parameters or overly complex queries:
748
+
749
+ ```typescript
750
+ // ❌ Problem - New object every render
751
+ function BadComponent({ user }) {
752
+ const result = useTypedQuery(
753
+ (db, params) => /* query */,
754
+ {
755
+ filter: { status: 'active', dept: user.department } // New object each render
756
+ }
757
+ );
758
+ }
759
+
760
+ // ✅ Solution - Stable parameters
761
+ function GoodComponent({ user }) {
762
+ const filter = useMemo(() => ({
763
+ status: 'active',
764
+ dept: user.department
765
+ }), [user.department]);
766
+
767
+ const result = useTypedQuery(
768
+ (db, params) => /* query */,
769
+ { filter }
770
+ );
771
+ }
772
+
773
+ // ✅ Even better - Direct values
774
+ function BetterComponent({ user }) {
775
+ const result = useTypedQuery(
776
+ (db, params) => /* query */,
777
+ {
778
+ status: 'active',
779
+ dept: user.department
780
+ }
781
+ );
782
+ }
783
+ ```
784
+
785
+ </details>
786
+
787
+ ## Further Reading
788
+
789
+ - [Kysely Documentation](https://kysely.dev/) - Learn more about the query builder
790
+ - [PGlite Documentation](https://pglite.dev/) - Understanding the underlying database
791
+ - [React Hooks](/academy/APIReferences/ReactHooks) - Other available hooks in Powerhouse
792
+ - [Component Library](/academy/ComponentLibrary/DocumentEngineering) - Building UI components
793
+
794
+ ## Related Hooks
795
+
796
+ - [`useDocuments`](/academy/APIReferences/ReactHooks#usedocuments) - Working with Powerhouse documents
797
+ - [`useDrives`](/academy/APIReferences/ReactHooks#usedrives) - Managing document drives
798
+ - [`useSelectedDocument`](/academy/APIReferences/ReactHooks#useselecteddocument) - Document selection state
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powerhousedao/academy",
3
- "version": "3.3.0-dev.2",
3
+ "version": "3.3.0-dev.3",
4
4
  "homepage": "https://powerhouse.academy",
5
5
  "repository": {
6
6
  "type": "git",