@ahoo-wang/fetcher-generator 2.1.1 β 2.2.2
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 +190 -58
- package/README.zh-CN.md +406 -0
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.ts +10 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +59 -33
- package/dist/cli.js.map +1 -1
- package/dist/client/clientGenerator.d.ts.map +1 -1
- package/dist/client/commandClientGenerator.d.ts.map +1 -1
- package/dist/client/queryClientGenerator.d.ts.map +1 -1
- package/dist/index.cjs +6 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +732 -491
- package/dist/index.js.map +1 -1
- package/dist/model/modelGenerator.d.ts +2 -0
- package/dist/model/modelGenerator.d.ts.map +1 -1
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +4 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/sourceFiles.d.ts.map +1 -1
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -8,19 +8,28 @@
|
|
|
8
8
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-generator)
|
|
9
9
|
[](https://deepwiki.com/Ahoo-Wang/fetcher)
|
|
10
10
|
|
|
11
|
-
TypeScript code generator from OpenAPI specs for
|
|
11
|
+
TypeScript code generator from OpenAPI specs for [Wow](https://github.com/Ahoo-Wang/Wow) domain-driven design framework.
|
|
12
|
+
Generates type-safe models, query
|
|
12
13
|
clients, and command clients from OpenAPI specifications.
|
|
13
14
|
|
|
15
|
+
**[Wow](https://github.com/Ahoo-Wang/Wow) Framework**: A domain-driven design framework that provides event sourcing,
|
|
16
|
+
CQRS (Command Query Responsibility
|
|
17
|
+
Segregation),
|
|
18
|
+
and aggregate patterns for building scalable distributed systems.
|
|
19
|
+
|
|
14
20
|
## π Features
|
|
15
21
|
|
|
16
|
-
- **π― OpenAPI 3.0+ Support**: Full support for OpenAPI 3.0+ specifications
|
|
22
|
+
- **π― OpenAPI 3.0+ Support**: Full support for OpenAPI 3.0+ specifications (JSON/YAML)
|
|
17
23
|
- **π¦ TypeScript Code Generation**: Generates type-safe TypeScript interfaces, enums, and classes
|
|
18
|
-
- **ποΈ Domain-Driven Design**: Specialized for WOW framework with aggregates, commands, and
|
|
24
|
+
- **ποΈ Domain-Driven Design**: Specialized for WOW framework with aggregates, commands, queries, and events
|
|
19
25
|
- **π§ CLI Tool**: Easy-to-use command-line interface for code generation
|
|
20
26
|
- **π¨ Decorator-Based APIs**: Generates decorator-based client classes for clean API interactions
|
|
21
|
-
- **π Comprehensive Models**: Handles complex schemas including unions, intersections, and references
|
|
22
|
-
- **π Fetcher Integration**: Seamlessly integrates with the Fetcher ecosystem
|
|
23
|
-
- **π Progress Logging**: Friendly logging with progress indicators
|
|
27
|
+
- **π Comprehensive Models**: Handles complex schemas including unions, intersections, enums, and references
|
|
28
|
+
- **π Fetcher Integration**: Seamlessly integrates with the Fetcher ecosystem packages
|
|
29
|
+
- **π Progress Logging**: Friendly logging with progress indicators during generation
|
|
30
|
+
- **π Auto Index Generation**: Automatically generates index.ts files for clean module organization
|
|
31
|
+
- **π Remote Spec Support**: Load OpenAPI specs directly from HTTP/HTTPS URLs
|
|
32
|
+
- **π Event Streaming**: Generates both regular and event-stream command clients
|
|
24
33
|
|
|
25
34
|
## π Quick Start
|
|
26
35
|
|
|
@@ -57,7 +66,10 @@ fetcher-generator generate [options]
|
|
|
57
66
|
- `-i, --input <path>`: Input OpenAPI specification file path or URL (required)
|
|
58
67
|
- Supports local file paths (e.g., `./api-spec.json`, `/path/to/spec.yaml`)
|
|
59
68
|
- Supports HTTP/HTTPS URLs (e.g., `https://api.example.com/openapi.json`)
|
|
60
|
-
- `-o, --output <path>`: Output directory path (
|
|
69
|
+
- `-o, --output <path>`: Output directory path (default: `src/generated`)
|
|
70
|
+
- `-c, --config <file>`: Configuration file path (optional)
|
|
71
|
+
- `-v, --verbose`: Enable verbose logging during generation
|
|
72
|
+
- `--dry-run`: Show what would be generated without writing files (reserved for future use)
|
|
61
73
|
- `-h, --help`: Display help information
|
|
62
74
|
- `-V, --version`: Display version number
|
|
63
75
|
|
|
@@ -84,13 +96,37 @@ The generator creates the following structure in your output directory:
|
|
|
84
96
|
```
|
|
85
97
|
output/
|
|
86
98
|
βββ {bounded-context}/
|
|
87
|
-
β βββ
|
|
99
|
+
β βββ index.ts # Auto-generated index file exporting all aggregates
|
|
100
|
+
β βββ boundedContext.ts # Bounded context alias constant
|
|
88
101
|
β βββ types.ts # Shared types for the bounded context
|
|
89
102
|
β βββ {aggregate}/ # Aggregate-specific files
|
|
90
|
-
β βββ
|
|
91
|
-
β βββ
|
|
92
|
-
β
|
|
93
|
-
βββ
|
|
103
|
+
β βββ index.ts # Auto-generated index file for aggregate
|
|
104
|
+
β βββ types.ts # Aggregate-specific types, models, and enums
|
|
105
|
+
β βββ queryClient.ts # Query client factory for state and event queries
|
|
106
|
+
β βββ commandClient.ts # Command client classes (regular and streaming)
|
|
107
|
+
βββ index.ts # Root index file exporting all bounded contexts
|
|
108
|
+
βββ tsconfig.json # TypeScript configuration for generated code
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Index File Generation
|
|
112
|
+
|
|
113
|
+
The generator automatically creates `index.ts` files in all directories to provide convenient module exports:
|
|
114
|
+
|
|
115
|
+
- **Root index.ts**: Exports all bounded contexts
|
|
116
|
+
- **Bounded context index.ts**: Exports all aggregates within the context
|
|
117
|
+
- **Aggregate index.ts**: Exports all files within the aggregate
|
|
118
|
+
|
|
119
|
+
This allows for clean imports like:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Import everything from a bounded context
|
|
123
|
+
import * as compensation from './generated/compensation';
|
|
124
|
+
|
|
125
|
+
// Import specific aggregates
|
|
126
|
+
import { executionFailed } from './generated/compensation';
|
|
127
|
+
|
|
128
|
+
// Import specific files
|
|
129
|
+
import { ExecutionFailedState } from './generated/compensation/execution_failed';
|
|
94
130
|
```
|
|
95
131
|
|
|
96
132
|
## π― Generated Code Examples
|
|
@@ -128,21 +164,25 @@ import {
|
|
|
128
164
|
QueryClientOptions,
|
|
129
165
|
ResourceAttributionPathSpec,
|
|
130
166
|
} from '@ahoo-wang/fetcher-wow';
|
|
167
|
+
import {
|
|
168
|
+
CartAggregatedFields,
|
|
169
|
+
CartItemAdded,
|
|
170
|
+
CartItemRemoved,
|
|
171
|
+
CartQuantityChanged,
|
|
172
|
+
CartState,
|
|
173
|
+
} from './types';
|
|
131
174
|
|
|
132
175
|
const DEFAULT_QUERY_CLIENT_OPTIONS: QueryClientOptions = {
|
|
133
|
-
contextAlias: '
|
|
134
|
-
aggregateName: '
|
|
135
|
-
resourceAttribution: ResourceAttributionPathSpec.
|
|
176
|
+
contextAlias: 'example',
|
|
177
|
+
aggregateName: 'cart',
|
|
178
|
+
resourceAttribution: ResourceAttributionPathSpec.OWNER,
|
|
136
179
|
};
|
|
137
180
|
|
|
138
|
-
type DOMAIN_EVENT_TYPES =
|
|
139
|
-
| CompensationPrepared
|
|
140
|
-
| ExecutionFailedApplied
|
|
141
|
-
| ExecutionFailedCreated;
|
|
181
|
+
type DOMAIN_EVENT_TYPES = CartItemAdded | CartItemRemoved | CartQuantityChanged;
|
|
142
182
|
|
|
143
|
-
export const
|
|
144
|
-
|
|
145
|
-
|
|
183
|
+
export const cartQueryClientFactory = new QueryClientFactory<
|
|
184
|
+
CartState,
|
|
185
|
+
CartAggregatedFields | string,
|
|
146
186
|
DOMAIN_EVENT_TYPES
|
|
147
187
|
>(DEFAULT_QUERY_CLIENT_OPTIONS);
|
|
148
188
|
```
|
|
@@ -151,56 +191,130 @@ export const executionFailedQueryClientFactory = new QueryClientFactory<
|
|
|
151
191
|
|
|
152
192
|
```typescript
|
|
153
193
|
// Generated command client with decorator-based API
|
|
194
|
+
import { ContentTypeValues } from '@ahoo-wang/fetcher';
|
|
154
195
|
import {
|
|
196
|
+
type ApiMetadata,
|
|
197
|
+
type ApiMetadataCapable,
|
|
155
198
|
api,
|
|
199
|
+
attribute,
|
|
200
|
+
autoGeneratedError,
|
|
201
|
+
del,
|
|
202
|
+
path,
|
|
156
203
|
post,
|
|
157
204
|
put,
|
|
158
|
-
path,
|
|
159
205
|
request,
|
|
160
|
-
attribute,
|
|
161
|
-
autoGeneratedError,
|
|
162
206
|
} from '@ahoo-wang/fetcher-decorator';
|
|
207
|
+
import { JsonEventStreamResultExtractor } from '@ahoo-wang/fetcher-eventstream';
|
|
208
|
+
import type {
|
|
209
|
+
CommandRequest,
|
|
210
|
+
CommandResult,
|
|
211
|
+
CommandResultEventStream,
|
|
212
|
+
DeleteAggregate,
|
|
213
|
+
RecoverAggregate,
|
|
214
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
215
|
+
import {
|
|
216
|
+
AddCartItem,
|
|
217
|
+
ChangeQuantity,
|
|
218
|
+
MockVariableCommand,
|
|
219
|
+
MountedCommand,
|
|
220
|
+
RemoveCartItem,
|
|
221
|
+
ViewCart,
|
|
222
|
+
} from './types';
|
|
223
|
+
|
|
224
|
+
enum COMMAND_ENDPOINT_PATHS {
|
|
225
|
+
VIEW_CART = '/owner/{ownerId}/cart/view_cart',
|
|
226
|
+
ADD_CART_ITEM = '/owner/{ownerId}/cart/add_cart_item',
|
|
227
|
+
CHANGE_QUANTITY = '/owner/{ownerId}/cart/change_quantity',
|
|
228
|
+
REMOVE_CART_ITEM = '/owner/{ownerId}/cart/remove_cart_item',
|
|
229
|
+
MOUNTED_COMMAND = '/owner/{ownerId}/cart/mounted_command',
|
|
230
|
+
MOCK_VARIABLE_COMMAND = '/tenant/{tenantId}/owner/{ownerId}/cart/{id}/{customerId}/{mockEnum}',
|
|
231
|
+
DEFAULT_DELETE_AGGREGATE = '/owner/{ownerId}/cart',
|
|
232
|
+
DEFAULT_RECOVER_AGGREGATE = '/owner/{ownerId}/cart/recover',
|
|
233
|
+
}
|
|
163
234
|
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
} as const;
|
|
235
|
+
const DEFAULT_COMMAND_CLIENT_OPTIONS: ApiMetadata = {
|
|
236
|
+
basePath: 'example',
|
|
237
|
+
};
|
|
168
238
|
|
|
169
239
|
@api()
|
|
170
|
-
export class
|
|
240
|
+
export class CartCommandClient implements ApiMetadataCapable {
|
|
171
241
|
constructor(
|
|
172
|
-
public readonly apiMetadata: ApiMetadata =
|
|
242
|
+
public readonly apiMetadata: ApiMetadata = DEFAULT_COMMAND_CLIENT_OPTIONS,
|
|
173
243
|
) {}
|
|
174
244
|
|
|
175
|
-
/**
|
|
176
|
-
@
|
|
177
|
-
|
|
178
|
-
@request() commandRequest: CommandRequest<
|
|
245
|
+
/** view_cart */
|
|
246
|
+
@put(COMMAND_ENDPOINT_PATHS.VIEW_CART)
|
|
247
|
+
viewCart(
|
|
248
|
+
@request() commandRequest: CommandRequest<ViewCart>,
|
|
249
|
+
@attribute() attributes: Record<string, any>,
|
|
250
|
+
): Promise<CommandResult> {
|
|
251
|
+
throw autoGeneratedError(commandRequest, attributes);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* ε ε
₯θ΄η©θ½¦
|
|
256
|
+
* ε ε
₯θ΄η©θ½¦
|
|
257
|
+
*/
|
|
258
|
+
@post(COMMAND_ENDPOINT_PATHS.ADD_CART_ITEM)
|
|
259
|
+
addCartItem(
|
|
260
|
+
@request() commandRequest: CommandRequest<AddCartItem>,
|
|
179
261
|
@attribute() attributes: Record<string, any>,
|
|
180
262
|
): Promise<CommandResult> {
|
|
181
263
|
throw autoGeneratedError(commandRequest, attributes);
|
|
182
264
|
}
|
|
183
265
|
|
|
184
|
-
/**
|
|
185
|
-
@put(COMMAND_ENDPOINT_PATHS.
|
|
186
|
-
|
|
187
|
-
@
|
|
188
|
-
@request() commandRequest: CommandRequest<PrepareCompensation>,
|
|
266
|
+
/** εζ΄θ΄δΉ°ζ°ι */
|
|
267
|
+
@put(COMMAND_ENDPOINT_PATHS.CHANGE_QUANTITY)
|
|
268
|
+
changeQuantity(
|
|
269
|
+
@request() commandRequest: CommandRequest<ChangeQuantity>,
|
|
189
270
|
@attribute() attributes: Record<string, any>,
|
|
190
271
|
): Promise<CommandResult> {
|
|
191
|
-
throw autoGeneratedError(
|
|
272
|
+
throw autoGeneratedError(commandRequest, attributes);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** ε ι€εε */
|
|
276
|
+
@put(COMMAND_ENDPOINT_PATHS.REMOVE_CART_ITEM)
|
|
277
|
+
removeCartItem(
|
|
278
|
+
@request() commandRequest: CommandRequest<RemoveCartItem>,
|
|
279
|
+
@attribute() attributes: Record<string, any>,
|
|
280
|
+
): Promise<CommandResult> {
|
|
281
|
+
throw autoGeneratedError(commandRequest, attributes);
|
|
192
282
|
}
|
|
193
283
|
}
|
|
194
284
|
```
|
|
195
285
|
|
|
286
|
+
The generator also creates streaming command clients for event-driven interactions:
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
@api('', {
|
|
290
|
+
headers: { Accept: ContentTypeValues.TEXT_EVENT_STREAM },
|
|
291
|
+
resultExtractor: JsonEventStreamResultExtractor,
|
|
292
|
+
})
|
|
293
|
+
export class CartStreamCommandClient implements ApiMetadataCapable {
|
|
294
|
+
constructor(
|
|
295
|
+
public readonly apiMetadata: ApiMetadata = DEFAULT_COMMAND_CLIENT_OPTIONS,
|
|
296
|
+
) {}
|
|
297
|
+
|
|
298
|
+
/** view_cart */
|
|
299
|
+
@put(COMMAND_ENDPOINT_PATHS.VIEW_CART)
|
|
300
|
+
viewCart(
|
|
301
|
+
@request() commandRequest: CommandRequest<ViewCart>,
|
|
302
|
+
@attribute() attributes: Record<string, any>,
|
|
303
|
+
): Promise<CommandResultEventStream> {
|
|
304
|
+
throw autoGeneratedError(commandRequest, attributes);
|
|
305
|
+
}
|
|
306
|
+
// ... other streaming methods
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
196
310
|
## π§ Integration with Fetcher
|
|
197
311
|
|
|
198
312
|
The generated code is designed to work seamlessly with the Fetcher ecosystem:
|
|
199
313
|
|
|
200
314
|
```typescript
|
|
201
315
|
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
202
|
-
import {
|
|
203
|
-
import {
|
|
316
|
+
import { cartQueryClientFactory } from './generated/example/cart/queryClient';
|
|
317
|
+
import { CartCommandClient } from './generated/example/cart/commandClient';
|
|
204
318
|
|
|
205
319
|
// Create a fetcher instance
|
|
206
320
|
const fetcher = new Fetcher({
|
|
@@ -211,43 +325,55 @@ const fetcher = new Fetcher({
|
|
|
211
325
|
Fetcher.register('api', fetcher);
|
|
212
326
|
|
|
213
327
|
// Use the generated query client factory
|
|
214
|
-
const queryClient =
|
|
215
|
-
const
|
|
328
|
+
const queryClient = cartQueryClientFactory.createQueryClient();
|
|
329
|
+
const cartState = await queryClient.loadAggregate('cart-id');
|
|
216
330
|
|
|
217
331
|
// Use the generated command client
|
|
218
|
-
const commandClient = new
|
|
219
|
-
const result = await commandClient.
|
|
332
|
+
const commandClient = new CartCommandClient();
|
|
333
|
+
const result = await commandClient.addCartItem(
|
|
220
334
|
{
|
|
221
335
|
command: {
|
|
222
|
-
|
|
336
|
+
productId: 'product-123',
|
|
337
|
+
quantity: 2,
|
|
223
338
|
},
|
|
224
339
|
},
|
|
225
340
|
{
|
|
226
|
-
|
|
341
|
+
ownerId: 'user-456',
|
|
227
342
|
},
|
|
228
343
|
);
|
|
229
344
|
```
|
|
230
345
|
|
|
231
346
|
## π OpenAPI Specification Requirements
|
|
232
347
|
|
|
233
|
-
The generator expects OpenAPI 3.0+ specifications with specific patterns for WOW framework:
|
|
348
|
+
The generator expects OpenAPI 3.0+ specifications with specific patterns for WOW domain-driven design framework:
|
|
234
349
|
|
|
235
350
|
### Aggregate Definition
|
|
236
351
|
|
|
237
|
-
Aggregates are identified by operation
|
|
352
|
+
Aggregates are identified by operation tags that follow the pattern:
|
|
353
|
+
|
|
354
|
+
- `{context}.{aggregate}`
|
|
238
355
|
|
|
239
|
-
|
|
356
|
+
### Operation Patterns
|
|
357
|
+
|
|
358
|
+
The generator recognizes operations by their `operationId` suffixes:
|
|
359
|
+
|
|
360
|
+
- **State Snapshots**: Operations ending with `.snapshot_state.single`
|
|
361
|
+
- **Event Queries**: Operations ending with `.event.list_query`
|
|
362
|
+
- **Field Queries**: Operations ending with `.snapshot.count`
|
|
363
|
+
- **Commands**: Any operation with a valid command request/response structure
|
|
240
364
|
|
|
241
365
|
### Commands and Queries
|
|
242
366
|
|
|
243
|
-
- **Commands**: Operations with `POST`, `PUT`, `DELETE` methods
|
|
244
|
-
- **Queries**: Operations with `GET` method
|
|
245
|
-
- **Events**: Operations returning event
|
|
367
|
+
- **Commands**: Operations with `POST`, `PUT`, `DELETE` methods that return `wow.CommandOk` responses
|
|
368
|
+
- **Queries**: Operations with `GET` method for retrieving aggregate state or events
|
|
369
|
+
- **Events**: Operations returning event stream arrays with domain event structures
|
|
246
370
|
|
|
247
|
-
### Schema
|
|
371
|
+
### Schema Conventions
|
|
248
372
|
|
|
249
373
|
- Use descriptive names for schemas
|
|
250
|
-
- Avoid `wow.` prefixed schemas (reserved for internal
|
|
374
|
+
- Avoid `wow.` prefixed schemas (reserved for internal framework schemas)
|
|
375
|
+
- Command request bodies should reference schemas in `components/schemas`
|
|
376
|
+
- State and event schemas should follow the expected structure for domain modeling
|
|
251
377
|
|
|
252
378
|
## π οΈ Development
|
|
253
379
|
|
|
@@ -267,8 +393,14 @@ pnpm lint
|
|
|
267
393
|
### Testing the Generator
|
|
268
394
|
|
|
269
395
|
```bash
|
|
270
|
-
# Generate test output
|
|
396
|
+
# Generate test output using the demo spec
|
|
271
397
|
pnpm generate
|
|
398
|
+
|
|
399
|
+
# Run tests
|
|
400
|
+
pnpm test
|
|
401
|
+
|
|
402
|
+
# Run tests with coverage
|
|
403
|
+
pnpm test -- --coverage
|
|
272
404
|
```
|
|
273
405
|
|
|
274
406
|
## π€ Contributing
|