@ahoo-wang/fetcher-wow 0.9.6 โ†’ 0.9.9

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/README.md ADDED
@@ -0,0 +1,541 @@
1
+ # @ahoo-wang/fetcher-wow
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@ahoo-wang/fetcher-wow.svg)](https://www.npmjs.com/package/@ahoo-wang/fetcher-wow)
4
+ [![Build Status](https://github.com/Ahoo-Wang/fetcher/actions/workflows/ci.yml/badge.svg)](https://github.com/Ahoo-Wang/fetcher/actions)
5
+ [![codecov](https://codecov.io/gh/Ahoo-Wang/fetcher/graph/badge.svg?token=JGiWZ52CvJ)](https://codecov.io/gh/Ahoo-Wang/fetcher)
6
+ [![License](https://img.shields.io/npm/l/@ahoo-wang/fetcher-wow.svg)](https://github.com/Ahoo-Wang/fetcher/blob/main/LICENSE)
7
+ [![npm downloads](https://img.shields.io/npm/dm/@ahoo-wang/fetcher-wow.svg)](https://www.npmjs.com/package/@ahoo-wang/fetcher-wow)
8
+ [![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40ahoo-wang%2Ffetcher-wow)](https://www.npmjs.com/package/@ahoo-wang/fetcher-wow)
9
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Ahoo-Wang/fetcher)
10
+
11
+ Support for [Wow](https://github.com/Ahoo-Wang/Wow) in Fetcher. Provides TypeScript types and utilities for working with
12
+ the Wow CQRS/DDD framework.
13
+
14
+ ## ๐ŸŒŸ Features
15
+
16
+ - **๐Ÿ“ฆ Comprehensive Type Definitions**: Full TypeScript support for Wow framework entities
17
+ - **๐Ÿ”ง Command Utilities**: Helpers for working with Wow commands and command results
18
+ - **๐Ÿ” Query DSL**: Rich query condition builders with operator support
19
+ - **๐Ÿ“ก Event Stream Support**: Integration with Server-Sent Events for real-time command results
20
+ - **๐Ÿ”„ CQRS Pattern**: Support for Command Query Responsibility Segregation patterns
21
+ - **๐Ÿงฑ DDD Building Blocks**: Domain-driven design types for aggregates, events, and more
22
+
23
+ ## ๐Ÿš€ Quick Start
24
+
25
+ ### Installation
26
+
27
+ ```bash
28
+ # Using npm
29
+ npm install @ahoo-wang/fetcher-wow
30
+
31
+ # Using pnpm
32
+ pnpm add @ahoo-wang/fetcher-wow
33
+
34
+ # Using yarn
35
+ yarn add @ahoo-wang/fetcher-wow
36
+ ```
37
+
38
+ ## ๐Ÿ“š API Reference
39
+
40
+ ### Command Module
41
+
42
+ #### CommandHeaders
43
+
44
+ Constants for standard HTTP headers used in Wow command processing:
45
+
46
+ ```typescript
47
+ import { CommandHeaders } from '@ahoo-wang/fetcher-wow';
48
+
49
+ // Example usage
50
+ const request = {
51
+ method: 'POST',
52
+ headers: {
53
+ [CommandHeaders.TENANT_ID]: 'tenant-123',
54
+ [CommandHeaders.AGGREGATE_ID]: 'aggregate-456',
55
+ [CommandHeaders.REQUEST_ID]: 'request-789',
56
+ },
57
+ body: JSON.stringify(command),
58
+ };
59
+ ```
60
+
61
+ Key headers include:
62
+
63
+ - `TENANT_ID` - Tenant identifier
64
+ - `OWNER_ID` - Owner identifier
65
+ - `AGGREGATE_ID` - Aggregate root identifier
66
+ - `AGGREGATE_VERSION` - Expected aggregate version
67
+ - `REQUEST_ID` - Request tracking ID
68
+ - `WAIT_*` - Various wait condition headers
69
+ - `LOCAL_FIRST` - Local processing preference
70
+ - And many more...
71
+
72
+ #### CommandRequest
73
+
74
+ Interface for command requests with full configuration options:
75
+
76
+ ```typescript
77
+ import { CommandRequest, CommandHeaders } from '@ahoo-wang/fetcher-wow';
78
+
79
+ const commandRequest: CommandRequest = {
80
+ path: '/commands/CreateUser',
81
+ method: 'POST',
82
+ headers: {
83
+ [CommandHeaders.TENANT_ID]: 'tenant-123',
84
+ },
85
+ body: {
86
+ name: 'John Doe',
87
+ email: 'john@example.com',
88
+ },
89
+ timeout: 5000,
90
+ aggregateId: 'user-456',
91
+ requestId: 'req-789',
92
+ localFirst: true,
93
+ stream: false,
94
+ };
95
+ ```
96
+
97
+ #### CommandResult
98
+
99
+ Interface representing the result of command execution:
100
+
101
+ ```typescript
102
+ import { CommandResult, CommandStage } from '@ahoo-wang/fetcher-wow';
103
+
104
+ const commandResult: CommandResult = {
105
+ id: 'result-123',
106
+ commandId: 'cmd-456',
107
+ requestId: 'req-789',
108
+ stage: CommandStage.PROCESSED,
109
+ contextName: 'user-context',
110
+ aggregateName: 'User',
111
+ aggregateId: 'user-456',
112
+ aggregateVersion: 1,
113
+ errorCode: 'Ok',
114
+ errorMsg: '',
115
+ function: {
116
+ functionKind: 'COMMAND',
117
+ contextName: 'user-context',
118
+ processorName: 'UserProcessor',
119
+ name: 'CreateUser',
120
+ },
121
+ signalTime: Date.now(),
122
+ };
123
+ ```
124
+
125
+ #### CommandResultEventStream
126
+
127
+ Utilities for working with command result event streams:
128
+
129
+ ```typescript
130
+ import {
131
+ toCommandResultEventStream,
132
+ CommandResultEvent,
133
+ } from '@ahoo-wang/fetcher-wow';
134
+ import { fetchEventStream } from '@ahoo-wang/fetcher-eventstream';
135
+
136
+ // Convert ServerSentEventStream to CommandResultEventStream
137
+ const eventStream = fetchEventStream('/commands/stream');
138
+ const commandResultStream = toCommandResultEventStream(eventStream);
139
+
140
+ // Process command results as they arrive
141
+ const reader = commandResultStream.getReader();
142
+ while (true) {
143
+ const { done, value } = await reader.read();
144
+ if (done) break;
145
+
146
+ const commandResult: CommandResult = value.data;
147
+ console.log('Command result:', commandResult);
148
+ }
149
+ ```
150
+
151
+ ### Query Module
152
+
153
+ #### Condition Builder
154
+
155
+ Comprehensive query condition builder with operator support:
156
+
157
+ ```typescript
158
+ import {
159
+ and,
160
+ or,
161
+ eq,
162
+ ne,
163
+ gt,
164
+ lt,
165
+ contains,
166
+ isIn,
167
+ between,
168
+ today,
169
+ active,
170
+ } from '@ahoo-wang/fetcher-wow';
171
+
172
+ // Simple conditions
173
+ const simpleConditions = [
174
+ eq('name', 'John'),
175
+ ne('status', 'inactive'),
176
+ gt('age', 18),
177
+ lt('score', 100),
178
+ ];
179
+
180
+ // Complex conditions
181
+ const complexCondition = and(
182
+ eq('tenantId', 'tenant-123'),
183
+ or(
184
+ contains('email', '@company.com'),
185
+ isIn('department', 'engineering', 'marketing'),
186
+ ),
187
+ between('salary', 50000, 100000),
188
+ today('createdAt'),
189
+ active(),
190
+ );
191
+
192
+ // Date conditions
193
+ const dateConditions = [
194
+ today('createdAt'),
195
+ beforeToday('lastLogin', 7), // Within last 7 days
196
+ thisWeek('updatedAt'),
197
+ lastMonth('createdDate'),
198
+ ];
199
+ ```
200
+
201
+ #### Operators
202
+
203
+ Full operator enumeration for query building:
204
+
205
+ ```typescript
206
+ import { Operator } from '@ahoo-wang/fetcher-wow';
207
+
208
+ // Logical operators
209
+ (Operator.AND, Operator.OR, Operator.NOR);
210
+
211
+ // Comparison operators
212
+ (Operator.EQ,
213
+ Operator.NE,
214
+ Operator.GT,
215
+ Operator.LT,
216
+ Operator.GTE,
217
+ Operator.LTE);
218
+
219
+ // Membership operators
220
+ (Operator.IN, Operator.NOT_IN, Operator.ALL_IN, Operator.BETWEEN);
221
+
222
+ // String operators
223
+ (Operator.CONTAINS, Operator.STARTS_WITH, Operator.ENDS_WITH);
224
+
225
+ // Existence operators
226
+ (Operator.NULL, Operator.NOT_NULL, Operator.EXISTS);
227
+
228
+ // Boolean operators
229
+ (Operator.TRUE, Operator.FALSE);
230
+
231
+ // Date operators
232
+ (Operator.TODAY,
233
+ Operator.BEFORE_TODAY,
234
+ Operator.TOMORROW,
235
+ Operator.THIS_WEEK,
236
+ Operator.NEXT_WEEK,
237
+ Operator.LAST_WEEK,
238
+ Operator.THIS_MONTH,
239
+ Operator.LAST_MONTH,
240
+ Operator.RECENT_DAYS,
241
+ Operator.EARLIER_DAYS);
242
+
243
+ // Special operators
244
+ (Operator.ID,
245
+ Operator.IDS,
246
+ Operator.AGGREGATE_ID,
247
+ Operator.AGGREGATE_IDS,
248
+ Operator.TENANT_ID,
249
+ Operator.OWNER_ID,
250
+ Operator.DELETED,
251
+ Operator.ALL,
252
+ Operator.ELEM_MATCH,
253
+ Operator.RAW);
254
+ ```
255
+
256
+ #### Queryable Interface
257
+
258
+ Interfaces for building queries with sorting, pagination, and projection:
259
+
260
+ ```typescript
261
+ import {
262
+ Queryable,
263
+ SortDirection,
264
+ DEFAULT_PAGINATION,
265
+ } from '@ahoo-wang/fetcher-wow';
266
+
267
+ const query: Queryable = {
268
+ condition: eq('status', 'active'),
269
+ sort: [
270
+ { field: 'createdAt', direction: SortDirection.DESC },
271
+ { field: 'name', direction: SortDirection.ASC },
272
+ ],
273
+ projection: {
274
+ include: ['id', 'name', 'email', 'status'],
275
+ exclude: ['password', 'internalNotes'],
276
+ },
277
+ };
278
+
279
+ const pagedQuery = {
280
+ ...query,
281
+ pagination: {
282
+ index: 2,
283
+ size: 20,
284
+ },
285
+ };
286
+ ```
287
+
288
+ ### Types Module
289
+
290
+ #### Core Types
291
+
292
+ Essential types for domain modeling:
293
+
294
+ ```typescript
295
+ import {
296
+ Identifier,
297
+ Version,
298
+ TenantId,
299
+ OwnerId,
300
+ NamedAggregate,
301
+ AggregateId,
302
+ StateCapable,
303
+ } from '@ahoo-wang/fetcher-wow';
304
+
305
+ interface User
306
+ extends Identifier,
307
+ Version,
308
+ TenantId,
309
+ OwnerId,
310
+ NamedAggregate,
311
+ StateCapable<UserState> {
312
+ id: string;
313
+ version: number;
314
+ tenantId: string;
315
+ ownerId: string;
316
+ contextName: string;
317
+ aggregateName: string;
318
+ state: UserState;
319
+ }
320
+
321
+ interface UserState {
322
+ name: string;
323
+ email: string;
324
+ status: 'active' | 'inactive';
325
+ createdAt: number;
326
+ }
327
+ ```
328
+
329
+ #### Error Handling
330
+
331
+ Standard error types and codes:
332
+
333
+ ```typescript
334
+ import { ErrorInfo, ErrorCodes, RecoverableType } from '@ahoo-wang/fetcher-wow';
335
+
336
+ const errorInfo: ErrorInfo = {
337
+ errorCode: ErrorCodes.NOT_FOUND,
338
+ errorMsg: 'User not found',
339
+ bindingErrors: [],
340
+ };
341
+
342
+ // Check error types
343
+ if (ErrorCodes.isSucceeded(errorInfo.errorCode)) {
344
+ console.log('Operation successful');
345
+ } else {
346
+ console.error('Operation failed:', errorInfo.errorMsg);
347
+ }
348
+ ```
349
+
350
+ #### Function Types
351
+
352
+ Function information for event and command handlers:
353
+
354
+ ```typescript
355
+ import { FunctionInfo, FunctionKind } from '@ahoo-wang/fetcher-wow';
356
+
357
+ const functionInfo: FunctionInfo = {
358
+ functionKind: FunctionKind.COMMAND,
359
+ contextName: 'user-context',
360
+ processorName: 'UserProcessor',
361
+ name: 'CreateUser',
362
+ };
363
+ ```
364
+
365
+ ## ๐Ÿ› ๏ธ Advanced Usage
366
+
367
+ ### Complete Command Flow Example
368
+
369
+ ```typescript
370
+ import {
371
+ CommandRequest,
372
+ CommandHeaders,
373
+ CommandResult,
374
+ CommandStage,
375
+ toCommandResultEventStream,
376
+ } from '@ahoo-wang/fetcher-wow';
377
+ import { fetchEventStream } from '@ahoo-wang/fetcher-eventstream';
378
+
379
+ // 1. Create command request
380
+ const commandRequest: CommandRequest = {
381
+ path: '/commands/user/CreateUser',
382
+ method: 'POST',
383
+ headers: {
384
+ [CommandHeaders.TENANT_ID]: 'tenant-123',
385
+ [CommandHeaders.REQUEST_ID]: 'req-' + Date.now(),
386
+ },
387
+ body: {
388
+ name: 'John Doe',
389
+ email: 'john@example.com',
390
+ },
391
+ timeout: 10000,
392
+ localFirst: true,
393
+ };
394
+
395
+ // 2. Execute command and wait for result
396
+ async function executeCommand(request: CommandRequest): Promise<CommandResult> {
397
+ // Implementation depends on your HTTP client
398
+ // This is just an example structure
399
+ const response = await fetch('/api' + request.path, {
400
+ method: request.method,
401
+ headers: request.headers,
402
+ body: JSON.stringify(request.body),
403
+ });
404
+
405
+ return response.json();
406
+ }
407
+
408
+ // 3. Stream command results in real-time
409
+ async function streamCommandResults() {
410
+ const eventStream = fetchEventStream('/commands/stream');
411
+ const commandResultStream = toCommandResultEventStream(eventStream);
412
+
413
+ const reader = commandResultStream.getReader();
414
+ try {
415
+ while (true) {
416
+ const { done, value } = await reader.read();
417
+ if (done) break;
418
+
419
+ const result: CommandResult = value.data;
420
+
421
+ // Handle different stages
422
+ switch (result.stage) {
423
+ case CommandStage.SENT:
424
+ console.log('Command sent to bus');
425
+ break;
426
+ case CommandStage.PROCESSED:
427
+ console.log('Command processed by aggregate');
428
+ break;
429
+ case CommandStage.SNAPSHOT:
430
+ console.log('Snapshot generated');
431
+ break;
432
+ case CommandStage.PROJECTED:
433
+ console.log('Events projected to read model');
434
+ break;
435
+ }
436
+ }
437
+ } finally {
438
+ reader.releaseLock();
439
+ }
440
+ }
441
+ ```
442
+
443
+ ### Complex Query Building
444
+
445
+ ```typescript
446
+ import {
447
+ and,
448
+ or,
449
+ eq,
450
+ ne,
451
+ gt,
452
+ lt,
453
+ contains,
454
+ isIn,
455
+ notIn,
456
+ between,
457
+ startsWith,
458
+ endsWith,
459
+ elemMatch,
460
+ isNull,
461
+ notNull,
462
+ exists,
463
+ today,
464
+ thisWeek,
465
+ recentDays,
466
+ } from '@ahoo-wang/fetcher-wow';
467
+
468
+ // Build a complex query for user search
469
+ const userSearchQuery = {
470
+ condition: and(
471
+ eq('tenantId', 'tenant-123'),
472
+ ne('status', 'deleted'),
473
+ or(
474
+ // Search by name or email
475
+ contains('name', 'john'),
476
+ contains('email', 'john'),
477
+ ),
478
+ // Age and score filters
479
+ gt('age', 18),
480
+ between('score', 50, 100),
481
+
482
+ // Department filters
483
+ isIn('departments', 'engineering', 'marketing'),
484
+ notIn('blockedDepartments', 'hr', 'finance'),
485
+
486
+ // String pattern matching
487
+ startsWith('employeeId', 'EMP-'),
488
+ endsWith('domain', '.com'),
489
+
490
+ // Array matching
491
+ elemMatch('roles', eq('name', 'admin')),
492
+
493
+ // Date filters
494
+ recentDays('lastLogin', 30),
495
+ thisWeek('createdAt'),
496
+
497
+ // Existence checks
498
+ exists('phoneNumber'),
499
+ notNull('address'),
500
+ ),
501
+
502
+ sort: [
503
+ { field: 'score', direction: 'DESC' },
504
+ { field: 'lastLogin', direction: 'DESC' },
505
+ ],
506
+
507
+ projection: {
508
+ include: ['id', 'name', 'email', 'score', 'lastLogin', 'departments'],
509
+ },
510
+
511
+ pagination: {
512
+ index: 1,
513
+ size: 50,
514
+ },
515
+ };
516
+ ```
517
+
518
+ ## ๐Ÿงช Testing
519
+
520
+ ```bash
521
+ # Run tests
522
+ pnpm test
523
+
524
+ # Run tests with coverage
525
+ pnpm test --coverage
526
+ ```
527
+
528
+ ## ๐Ÿค Contributing
529
+
530
+ Contributions are welcome! Please see
531
+ the [contributing guide](https://github.com/Ahoo-Wang/fetcher/blob/main/CONTRIBUTING.md) for more details.
532
+
533
+ ## ๐Ÿ“„ License
534
+
535
+ Apache-2.0
536
+
537
+ ---
538
+
539
+ <p align="center">
540
+ Part of the <a href="https://github.com/Ahoo-Wang/fetcher">Fetcher</a> ecosystem
541
+ </p>