@ahoo-wang/fetcher-wow 2.9.0 → 2.9.1

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 (2) hide show
  1. package/README.md +239 -7
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -25,11 +25,11 @@ working with the Wow CQRS/DDD framework.
25
25
  - **🔍 Powerful Query DSL**: Rich query condition builder with comprehensive operator support for complex querying
26
26
  - **🔍 Query Clients**: Specialized clients for querying snapshot and event stream data with comprehensive query
27
27
  operations:
28
- - Counting resources
29
- - Listing resources
30
- - Streaming resources as Server-Sent Events
31
- - Paging resources
32
- - Retrieving single resources
28
+ - Counting resources
29
+ - Listing resources
30
+ - Streaming resources as Server-Sent Events
31
+ - Paging resources
32
+ - Retrieving single resources
33
33
 
34
34
  ## 🚀 Quick Start
35
35
 
@@ -407,9 +407,241 @@ const paged = await cartEventStreamQueryClient.paged(pagedQuery);
407
407
  - `paged(pagedQuery: PagedQuery): Promise<PagedList<Partial<DomainEventStream>>>` - Retrieves a paged list of domain
408
408
  event streams.
409
409
 
410
- ## 🛠️ Advanced Usage
410
+ ## 🚀 Advanced Usage Examples
411
+
412
+ ### Custom Command Builders and Validators
413
+
414
+ Create type-safe command builders with validation:
415
+
416
+ ```typescript
417
+ import { CommandClient, CommandRequest } from '@ahoo-wang/fetcher-wow';
418
+
419
+ // Command type definitions
420
+ interface CreateUserCommand {
421
+ commandType: 'CreateUser';
422
+ commandId: string;
423
+ aggregateId: string;
424
+ name: string;
425
+ email: string;
426
+ role: 'admin' | 'user' | 'moderator';
427
+ }
428
+
429
+ interface UpdateUserProfileCommand {
430
+ commandType: 'UpdateUserProfile';
431
+ commandId: string;
432
+ aggregateId: string;
433
+ displayName?: string;
434
+ bio?: string;
435
+ avatarUrl?: string;
436
+ }
437
+
438
+ // Command builder with validation
439
+ class UserCommandBuilder {
440
+ private commandClient: CommandClient;
441
+
442
+ constructor(commandClient: CommandClient) {
443
+ this.commandClient = commandClient;
444
+ }
445
+
446
+ async createUser(params: {
447
+ name: string;
448
+ email: string;
449
+ role: 'admin' | 'user' | 'moderator';
450
+ ownerId: string;
451
+ }): Promise<any> {
452
+ // Validate input
453
+ this.validateCreateUserParams(params);
454
+
455
+ const command: CreateUserCommand = {
456
+ commandType: 'CreateUser',
457
+ commandId: crypto.randomUUID(),
458
+ aggregateId: crypto.randomUUID(), // New user gets new aggregate ID
459
+ ...params,
460
+ };
461
+
462
+ return this.commandClient.send(command, { ownerId: params.ownerId });
463
+ }
464
+
465
+ async updateProfile(params: {
466
+ userId: string;
467
+ displayName?: string;
468
+ bio?: string;
469
+ avatarUrl?: string;
470
+ ownerId: string;
471
+ }): Promise<any> {
472
+ // Validate input
473
+ this.validateUpdateProfileParams(params);
474
+
475
+ const command: UpdateUserProfileCommand = {
476
+ commandType: 'UpdateUserProfile',
477
+ commandId: crypto.randomUUID(),
478
+ aggregateId: params.userId, // User ID is the aggregate ID
479
+ displayName: params.displayName,
480
+ bio: params.bio,
481
+ avatarUrl: params.avatarUrl,
482
+ };
483
+
484
+ return this.commandClient.send(command, { ownerId: params.ownerId });
485
+ }
486
+
487
+ private validateCreateUserParams(params: any) {
488
+ if (!params.name || params.name.length < 2) {
489
+ throw new Error('Name must be at least 2 characters');
490
+ }
491
+ if (!params.email || !this.isValidEmail(params.email)) {
492
+ throw new Error('Valid email is required');
493
+ }
494
+ if (!['admin', 'user', 'moderator'].includes(params.role)) {
495
+ throw new Error('Invalid role');
496
+ }
497
+ }
498
+
499
+ private validateUpdateProfileParams(params: any) {
500
+ if (!params.userId) {
501
+ throw new Error('User ID is required');
502
+ }
503
+ if (params.displayName && params.displayName.length > 50) {
504
+ throw new Error('Display name too long');
505
+ }
506
+ if (params.bio && params.bio.length > 500) {
507
+ throw new Error('Bio too long');
508
+ }
509
+ }
510
+
511
+ private isValidEmail(email: string): boolean {
512
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
513
+ return emailRegex.test(email);
514
+ }
515
+ }
516
+
517
+ // Usage
518
+ const commandClient = new CommandClient({ basePath: '/api/commands' });
519
+ const userCommands = new UserCommandBuilder(commandClient);
520
+
521
+ try {
522
+ // Create a new user
523
+ const result = await userCommands.createUser({
524
+ name: 'John Doe',
525
+ email: 'john@example.com',
526
+ role: 'user',
527
+ ownerId: 'user-123',
528
+ });
529
+ console.log('User created:', result);
530
+
531
+ // Update user profile
532
+ await userCommands.updateProfile({
533
+ userId: result.aggregateId,
534
+ displayName: 'Johnny',
535
+ bio: 'Software developer',
536
+ ownerId: 'user-123',
537
+ });
538
+ } catch (error) {
539
+ console.error('Command failed:', error);
540
+ }
541
+ ```
542
+
543
+ ### Advanced Query Composition with Reactive Updates
544
+
545
+ Create complex queries with reactive real-time updates:
411
546
 
412
- ### Complete Example with Command and Query Flow
547
+ ```typescript
548
+ import {
549
+ SnapshotQueryClient,
550
+ EventStreamQueryClient,
551
+ } from '@ahoo-wang/fetcher-wow';
552
+
553
+ // Advanced query manager with reactive updates
554
+ class ReactiveQueryManager {
555
+ private snapshotClient: SnapshotQueryClient;
556
+ private streamClient: EventStreamQueryClient;
557
+ private listeners: Map<string, (data: any) => void> = new Map();
558
+
559
+ constructor(basePath: string) {
560
+ this.snapshotClient = new SnapshotQueryClient({ basePath });
561
+ this.streamClient = new EventStreamQueryClient({ basePath });
562
+ }
563
+
564
+ // Subscribe to real-time updates for a query
565
+ subscribeToQuery(
566
+ queryId: string,
567
+ initialQuery: any,
568
+ callback: (data: any) => void,
569
+ ) {
570
+ this.listeners.set(queryId, callback);
571
+
572
+ // Start streaming updates
573
+ this.startStreaming(queryId, initialQuery);
574
+ }
575
+
576
+ // Unsubscribe from updates
577
+ unsubscribe(queryId: string) {
578
+ this.listeners.delete(queryId);
579
+ // Close stream if needed
580
+ }
581
+
582
+ private async startStreaming(queryId: string, query: any) {
583
+ try {
584
+ const stream = await this.streamClient.listStream(query);
585
+
586
+ for await (const event of stream) {
587
+ const listener = this.listeners.get(queryId);
588
+ if (listener) {
589
+ listener(event);
590
+ }
591
+ }
592
+ } catch (error) {
593
+ console.error(`Stream error for ${queryId}:`, error);
594
+ }
595
+ }
596
+
597
+ // Complex query with aggregations
598
+ async getUserDashboardStats(userId: string) {
599
+ const [userProfile, recentActivity, stats] = await Promise.all([
600
+ this.snapshotClient.single({
601
+ condition: { id: userId },
602
+ projection: { name: 1, email: 1, createdAt: 1 },
603
+ }),
604
+ this.snapshotClient.list({
605
+ condition: { userId, type: 'activity' },
606
+ sort: [{ field: 'timestamp', order: 'desc' }],
607
+ limit: 10,
608
+ }),
609
+ this.snapshotClient.count({
610
+ condition: { userId },
611
+ }),
612
+ ]);
613
+
614
+ return {
615
+ profile: userProfile,
616
+ recentActivity,
617
+ totalActions: stats,
618
+ lastActivity: recentActivity[0]?.timestamp,
619
+ };
620
+ }
621
+ }
622
+
623
+ // Usage
624
+ const queryManager = new ReactiveQueryManager('/api/queries');
625
+
626
+ // Get dashboard data
627
+ const dashboard = await queryManager.getUserDashboardStats('user-123');
628
+ console.log('Dashboard:', dashboard);
629
+
630
+ // Subscribe to real-time updates
631
+ queryManager.subscribeToQuery(
632
+ 'user-activity',
633
+ {
634
+ condition: { userId: 'user-123', type: 'activity' },
635
+ sort: [{ field: 'timestamp', order: 'desc' }],
636
+ },
637
+ update => {
638
+ console.log('New activity:', update);
639
+ // Update UI with new data
640
+ },
641
+ );
642
+ ```
643
+
644
+ ## 🛠️ Advanced Usage
413
645
 
414
646
  ```typescript
415
647
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahoo-wang/fetcher-wow",
3
- "version": "2.9.0",
3
+ "version": "2.9.1",
4
4
  "description": "Support for Wow(https://github.com/Ahoo-Wang/Wow) in Fetcher",
5
5
  "keywords": [
6
6
  "fetch",