@ahoo-wang/fetcher-generator 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.
package/README.md CHANGED
@@ -340,8 +340,7 @@ import {
340
340
  export class CartApiClient implements ApiMetadataCapable {
341
341
  constructor(
342
342
  public readonly apiMetadata: ApiMetadata = { basePath: 'example' },
343
- ) {
344
- }
343
+ ) {}
345
344
 
346
345
  /** Custom command sending */
347
346
  @post('/cart/{userId}/customize-send-cmd')
@@ -397,7 +396,9 @@ const fetcher = new Fetcher({
397
396
  });
398
397
 
399
398
  // Use the generated query client factory
400
- const snapshotClient = cartQueryClientFactory.createSnapshotQueryClient({ fetcher: fetcher });
399
+ const snapshotClient = cartQueryClientFactory.createSnapshotQueryClient({
400
+ fetcher: fetcher,
401
+ });
401
402
  const cartState = await snapshotClient.singleState({ condition: all() });
402
403
 
403
404
  // Use the generated command client
@@ -419,6 +420,209 @@ const apiClient = new CartApiClient({ fetcher: fetcher });
419
420
  const cartData = await apiClient.me();
420
421
  ```
421
422
 
423
+ ## 🚀 Advanced Usage Examples
424
+
425
+ ### Custom Configuration
426
+
427
+ Create a `.fetcherrc.json` configuration file for advanced generation options:
428
+
429
+ ```json
430
+ {
431
+ "generator": {
432
+ "targetFramework": "wow",
433
+ "outputFormat": "typescript",
434
+ "basePath": "api/v1",
435
+ "generateIndexFiles": true,
436
+ "verbose": true,
437
+ "typeMappings": {
438
+ "string": "string",
439
+ "integer": "number",
440
+ "boolean": "boolean",
441
+ "number": "number"
442
+ },
443
+ "schemaTransformers": [
444
+ {
445
+ "pattern": "^wow\\.",
446
+ "transform": "removePrefix"
447
+ }
448
+ ]
449
+ }
450
+ }
451
+ ```
452
+
453
+ ### Multi-Context Generation
454
+
455
+ Generate code for multiple bounded contexts from a single OpenAPI spec:
456
+
457
+ ```bash
458
+ # Generate all contexts
459
+ fetcher-generator generate -i ./multi-context-api.json -o ./src/generated
460
+
461
+ # Generated structure:
462
+ # src/generated/
463
+ # ├── ecommerce/
464
+ # │ ├── index.ts
465
+ # │ ├── boundedContext.ts
466
+ # │ ├── cart/
467
+ # │ ├── order/
468
+ # │ └── product/
469
+ # └── inventory/
470
+ # ├── index.ts
471
+ # ├── boundedContext.ts
472
+ # └── stock/
473
+ ```
474
+
475
+ ### Event-Driven Architecture
476
+
477
+ Generated code supports event streaming for real-time updates:
478
+
479
+ ```typescript
480
+ import { cartQueryClientFactory } from './generated/ecommerce/cart/queryClient';
481
+ import { CartStreamCommandClient } from './generated/ecommerce/cart/commandClient';
482
+
483
+ // Create streaming query client
484
+ const eventClient = cartQueryClientFactory.createEventQueryClient({
485
+ fetcher: fetcher,
486
+ onEvent: event => {
487
+ console.log('Cart event:', event);
488
+ // Handle real-time cart updates
489
+ },
490
+ });
491
+
492
+ // Use streaming command client
493
+ const streamCommandClient = new CartStreamCommandClient({ fetcher });
494
+
495
+ const eventStream = await streamCommandClient.addCartItem(
496
+ {
497
+ command: { productId: 'item-123', quantity: 1 },
498
+ },
499
+ { ownerId: 'user-456' },
500
+ );
501
+
502
+ // Process streaming events
503
+ for await (const event of eventStream) {
504
+ console.log('Command event:', event);
505
+ }
506
+ ```
507
+
508
+ ### Custom Type Guards and Validation
509
+
510
+ Generated code includes runtime type validation:
511
+
512
+ ```typescript
513
+ import { CartState, isCartState } from './generated/ecommerce/cart/types';
514
+
515
+ // Runtime type checking
516
+ function processCartData(data: unknown) {
517
+ if (isCartState(data)) {
518
+ // TypeScript knows data is CartState here
519
+ console.log('Valid cart state:', data.items.length);
520
+ } else {
521
+ throw new Error('Invalid cart data');
522
+ }
523
+ }
524
+ ```
525
+
526
+ ### Integration with State Management
527
+
528
+ Use generated clients with popular state management libraries:
529
+
530
+ ```typescript
531
+ // Redux Toolkit integration
532
+ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
533
+ import { cartQueryClientFactory } from './generated/ecommerce/cart/queryClient';
534
+
535
+ const fetchCartState = createAsyncThunk(
536
+ 'cart/fetchState',
537
+ async (ownerId: string) => {
538
+ const snapshotClient = cartQueryClientFactory.createSnapshotQueryClient({
539
+ fetcher: fetcher,
540
+ });
541
+ return await snapshotClient.singleState({ condition: { ownerId } });
542
+ },
543
+ );
544
+
545
+ const cartSlice = createSlice({
546
+ name: 'cart',
547
+ initialState: { items: [], loading: false },
548
+ reducers: {},
549
+ extraReducers: builder => {
550
+ builder.addCase(fetchCartState.pending, state => {
551
+ state.loading = true;
552
+ });
553
+ builder.addCase(fetchCartState.fulfilled, (state, action) => {
554
+ state.items = action.payload.items;
555
+ state.loading = false;
556
+ });
557
+ },
558
+ });
559
+ ```
560
+
561
+ ### Error Handling and Retry Logic
562
+
563
+ Generated clients work seamlessly with retry mechanisms:
564
+
565
+ ```typescript
566
+ import { Fetcher } from '@ahoo-wang/fetcher';
567
+ import { CartCommandClient } from './generated/ecommerce/cart/commandClient';
568
+
569
+ // Configure fetcher with retry logic
570
+ const fetcher = new Fetcher({
571
+ baseURL: 'https://api.example.com',
572
+ retryConfig: {
573
+ maxRetries: 3,
574
+ retryDelay: 1000,
575
+ retryCondition: error => error.status >= 500,
576
+ },
577
+ });
578
+
579
+ const commandClient = new CartCommandClient({ fetcher });
580
+
581
+ // Commands automatically retry on server errors
582
+ try {
583
+ const result = await commandClient.addCartItem(
584
+ {
585
+ command: { productId: 'item-123', quantity: 1 },
586
+ },
587
+ { ownerId: 'user-456' },
588
+ );
589
+ } catch (error) {
590
+ console.error('Failed to add item after retries:', error);
591
+ }
592
+ ```
593
+
594
+ ### Testing Generated Code
595
+
596
+ Write unit tests for generated clients:
597
+
598
+ ```typescript
599
+ import { describe, it, expect, vi } from 'vitest';
600
+ import { CartCommandClient } from './generated/ecommerce/cart/commandClient';
601
+
602
+ describe('CartCommandClient', () => {
603
+ it('should add item to cart', async () => {
604
+ const mockFetcher = {
605
+ request: vi.fn().mockResolvedValue({ success: true }),
606
+ };
607
+
608
+ const client = new CartCommandClient({
609
+ fetcher: mockFetcher as any,
610
+ });
611
+
612
+ // This will throw autoGeneratedError in generated code
613
+ // In real usage, you'd use a decorator interceptor
614
+ expect(() =>
615
+ client.addCartItem(
616
+ {
617
+ command: { productId: 'test', quantity: 1 },
618
+ },
619
+ { ownerId: 'user' },
620
+ ),
621
+ ).toThrow();
622
+ });
623
+ });
624
+ ```
625
+
422
626
  ## 📋 OpenAPI Specification Requirements
423
627
 
424
628
  The generator expects OpenAPI 3.0+ specifications with specific patterns for WOW domain-driven design framework:
package/dist/cli.cjs CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("commander"),a=require("./index.cjs");require("@ahoo-wang/fetcher");require("yaml");require("fs");require("path");class f{getTimestamp(){return new Date().toISOString().slice(11,19)}info(e,...t){const o=this.getTimestamp();t.length>0?console.log(`[${o}] â„šī¸ ${e}`,...t):console.log(`[${o}] â„šī¸ ${e}`)}success(e,...t){const o=this.getTimestamp();t.length>0?console.log(`[${o}] ✅ ${e}`,...t):console.log(`[${o}] ✅ ${e}`)}error(e,...t){const o=this.getTimestamp();t.length>0?console.error(`[${o}] ❌ ${e}`,...t):console.error(`[${o}] ❌ ${e}`)}progress(e,t=0,...o){const i=this.getTimestamp(),r=" ".repeat(t);o.length>0?console.log(`[${i}] 🔄 ${r}${e}`,...o):console.log(`[${i}] 🔄 ${r}${e}`)}progressWithCount(e,t,o,i=0,...r){const s=this.getTimestamp(),p=" ".repeat(i),l=`[${e}/${t}]`;r.length>0?console.log(`[${s}] 🔄 ${p}${l} ${o}`,...r):console.log(`[${s}] 🔄 ${p}${l} ${o}`)}}function h(n){if(!n)return!1;try{const e=new URL(n);return e.protocol==="http:"||e.protocol==="https:"}catch{return n.length>0}}async function d(n){const e=new f;process.on("SIGINT",()=>{e.error("Generation interrupted by user"),process.exit(130)}),h(n.input)||(e.error("Invalid input: must be a valid file path or HTTP/HTTPS URL"),process.exit(2));try{e.info("Starting code generation...");const t={inputPath:n.input,outputDir:n.output,configPath:n.config,tsConfigFilePath:n.tsConfigFilePath,logger:e};await new a.CodeGenerator(t).generate(),e.success(`Code generation completed successfully! Files generated in: ${n.output}`)}catch(t){e.error(`Error during code generation: ${t}`),process.exit(1)}}const $="2.9.0",m={version:$};function u(){return c.program.name("fetcher-generator").description("OpenAPI Specification TypeScript code generator for Wow").version(m.version),c.program.command("generate").description("Generate TypeScript code from OpenAPI specification").requiredOption("-i, --input <file>","Input OpenAPI specification file path or URL (http/https)").option("-o, --output <path>","Output directory path","src/generated").option("-c, --config <file>","Configuration file path",a.DEFAULT_CONFIG_PATH).option("-t, --ts-config-file-path <file>","TypeScript configuration file path").option("-v, --verbose","Enable verbose logging").option("--dry-run","Show what would be generated without writing files").action(d),c.program}function g(){u().parse()}g();exports.runCLI=g;exports.setupCLI=u;
2
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("commander"),a=require("./index.cjs");require("@ahoo-wang/fetcher");require("yaml");require("fs");require("path");class f{getTimestamp(){return new Date().toISOString().slice(11,19)}info(e,...t){const o=this.getTimestamp();t.length>0?console.log(`[${o}] â„šī¸ ${e}`,...t):console.log(`[${o}] â„šī¸ ${e}`)}success(e,...t){const o=this.getTimestamp();t.length>0?console.log(`[${o}] ✅ ${e}`,...t):console.log(`[${o}] ✅ ${e}`)}error(e,...t){const o=this.getTimestamp();t.length>0?console.error(`[${o}] ❌ ${e}`,...t):console.error(`[${o}] ❌ ${e}`)}progress(e,t=0,...o){const i=this.getTimestamp(),r=" ".repeat(t);o.length>0?console.log(`[${i}] 🔄 ${r}${e}`,...o):console.log(`[${i}] 🔄 ${r}${e}`)}progressWithCount(e,t,o,i=0,...r){const s=this.getTimestamp(),p=" ".repeat(i),l=`[${e}/${t}]`;r.length>0?console.log(`[${s}] 🔄 ${p}${l} ${o}`,...r):console.log(`[${s}] 🔄 ${p}${l} ${o}`)}}function h(n){if(!n)return!1;try{const e=new URL(n);return e.protocol==="http:"||e.protocol==="https:"}catch{return n.length>0}}async function d(n){const e=new f;process.on("SIGINT",()=>{e.error("Generation interrupted by user"),process.exit(130)}),h(n.input)||(e.error("Invalid input: must be a valid file path or HTTP/HTTPS URL"),process.exit(2));try{e.info("Starting code generation...");const t={inputPath:n.input,outputDir:n.output,configPath:n.config,tsConfigFilePath:n.tsConfigFilePath,logger:e};await new a.CodeGenerator(t).generate(),e.success(`Code generation completed successfully! Files generated in: ${n.output}`)}catch(t){e.error(`Error during code generation: ${t}`),process.exit(1)}}const $="2.9.1",m={version:$};function u(){return c.program.name("fetcher-generator").description("OpenAPI Specification TypeScript code generator for Wow").version(m.version),c.program.command("generate").description("Generate TypeScript code from OpenAPI specification").requiredOption("-i, --input <file>","Input OpenAPI specification file path or URL (http/https)").option("-o, --output <path>","Output directory path","src/generated").option("-c, --config <file>","Configuration file path",a.DEFAULT_CONFIG_PATH).option("-t, --ts-config-file-path <file>","TypeScript configuration file path").option("-v, --verbose","Enable verbose logging").option("--dry-run","Show what would be generated without writing files").action(d),c.program}function g(){u().parse()}g();exports.runCLI=g;exports.setupCLI=u;
3
3
  //# sourceMappingURL=cli.cjs.map
package/dist/cli.js CHANGED
@@ -63,7 +63,7 @@ async function h(n) {
63
63
  e.error(`Error during code generation: ${t}`), process.exit(1);
64
64
  }
65
65
  }
66
- const $ = "2.9.0", d = {
66
+ const $ = "2.9.1", d = {
67
67
  version: $
68
68
  };
69
69
  function m() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahoo-wang/fetcher-generator",
3
- "version": "2.9.0",
3
+ "version": "2.9.1",
4
4
  "description": "TypeScript code generator from OpenAPI specs for Wow domain-driven design framework. ",
5
5
  "keywords": [
6
6
  "fetch",