@ahoo-wang/fetcher-generator 2.1.0

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 (67) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +289 -0
  3. package/dist/aggregate/aggregate.d.ts +73 -0
  4. package/dist/aggregate/aggregate.d.ts.map +1 -0
  5. package/dist/aggregate/aggregateResolver.d.ts +48 -0
  6. package/dist/aggregate/aggregateResolver.d.ts.map +1 -0
  7. package/dist/aggregate/index.d.ts +5 -0
  8. package/dist/aggregate/index.d.ts.map +1 -0
  9. package/dist/aggregate/types.d.ts +33 -0
  10. package/dist/aggregate/types.d.ts.map +1 -0
  11. package/dist/aggregate/utils.d.ts +45 -0
  12. package/dist/aggregate/utils.d.ts.map +1 -0
  13. package/dist/baseCodeGenerator.d.ts +22 -0
  14. package/dist/baseCodeGenerator.d.ts.map +1 -0
  15. package/dist/cli.cjs +2 -0
  16. package/dist/cli.d.ts +7 -0
  17. package/dist/cli.d.ts.map +1 -0
  18. package/dist/cli.js +56 -0
  19. package/dist/client/clientGenerator.d.ts +25 -0
  20. package/dist/client/clientGenerator.d.ts.map +1 -0
  21. package/dist/client/commandClientGenerator.d.ts +38 -0
  22. package/dist/client/commandClientGenerator.d.ts.map +1 -0
  23. package/dist/client/index.d.ts +2 -0
  24. package/dist/client/index.d.ts.map +1 -0
  25. package/dist/client/queryClientGenerator.d.ts +32 -0
  26. package/dist/client/queryClientGenerator.d.ts.map +1 -0
  27. package/dist/client/utils.d.ts +12 -0
  28. package/dist/client/utils.d.ts.map +1 -0
  29. package/dist/index.cjs +11 -0
  30. package/dist/index.d.ts +21 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +968 -0
  33. package/dist/model/index.d.ts +4 -0
  34. package/dist/model/index.d.ts.map +1 -0
  35. package/dist/model/modelGenerator.d.ts +130 -0
  36. package/dist/model/modelGenerator.d.ts.map +1 -0
  37. package/dist/model/modelInfo.d.ts +27 -0
  38. package/dist/model/modelInfo.d.ts.map +1 -0
  39. package/dist/model/wowTypeMapping.d.ts +33 -0
  40. package/dist/model/wowTypeMapping.d.ts.map +1 -0
  41. package/dist/types.d.ts +44 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/utils/clis.d.ts +18 -0
  44. package/dist/utils/clis.d.ts.map +1 -0
  45. package/dist/utils/components.d.ts +64 -0
  46. package/dist/utils/components.d.ts.map +1 -0
  47. package/dist/utils/index.d.ts +11 -0
  48. package/dist/utils/index.d.ts.map +1 -0
  49. package/dist/utils/logger.d.ts +21 -0
  50. package/dist/utils/logger.d.ts.map +1 -0
  51. package/dist/utils/naming.d.ts +21 -0
  52. package/dist/utils/naming.d.ts.map +1 -0
  53. package/dist/utils/openAPIParser.d.ts +14 -0
  54. package/dist/utils/openAPIParser.d.ts.map +1 -0
  55. package/dist/utils/operations.d.ts +29 -0
  56. package/dist/utils/operations.d.ts.map +1 -0
  57. package/dist/utils/references.d.ts +3 -0
  58. package/dist/utils/references.d.ts.map +1 -0
  59. package/dist/utils/resources.d.ts +4 -0
  60. package/dist/utils/resources.d.ts.map +1 -0
  61. package/dist/utils/responses.d.ts +8 -0
  62. package/dist/utils/responses.d.ts.map +1 -0
  63. package/dist/utils/schemas.d.ts +22 -0
  64. package/dist/utils/schemas.d.ts.map +1 -0
  65. package/dist/utils/sourceFiles.d.ts +54 -0
  66. package/dist/utils/sourceFiles.d.ts.map +1 -0
  67. package/package.json +82 -0
@@ -0,0 +1,45 @@
1
+ import { Tag } from '@ahoo-wang/fetcher-openapi';
2
+ import { PartialBy } from '@ahoo-wang/fetcher';
3
+ import { AggregateDefinition, TagAliasAggregate } from './aggregate';
4
+ /**
5
+ * Checks if a tag name follows the alias aggregate pattern and extracts its components.
6
+ *
7
+ * This function determines if a tag name follows the format "contextAlias.aggregateName"
8
+ * and returns the components if it matches.
9
+ *
10
+ * @param tagName - The tag name to check
11
+ * @returns A tuple with [contextAlias, aggregateName] if the pattern matches, null otherwise
12
+ */
13
+ export declare function isAliasAggregate(tagName: string): [string, string] | null;
14
+ /**
15
+ * Converts a Tag to a TagAliasAggregate if it follows the alias pattern.
16
+ *
17
+ * This function checks if a tag name follows the alias aggregate pattern and
18
+ * creates a TagAliasAggregate object with the tag and its parsed components.
19
+ *
20
+ * @param tag - The Tag to convert
21
+ * @returns A TagAliasAggregate if the tag name matches the pattern, null otherwise
22
+ */
23
+ export declare function tagToAggregate(tag: Tag): TagAliasAggregate | null;
24
+ /**
25
+ * Converts an array of Tags to a map of aggregates.
26
+ *
27
+ * This function processes an array of tags, converts those that follow the alias pattern
28
+ * to aggregates, and returns a map where keys are tag names and values are partial
29
+ * aggregate definitions.
30
+ *
31
+ * @param tags - Optional array of Tags to process
32
+ * @returns A map of aggregate definitions keyed by tag name
33
+ */
34
+ export declare function tagsToAggregates(tags?: Tag[]): Map<string, PartialBy<AggregateDefinition, 'state' | 'fields'>>;
35
+ /**
36
+ * Extracts the command name from an operation ID.
37
+ *
38
+ * This function parses an operation ID expected to follow the format
39
+ * "context.aggregate.command" and returns the command part.
40
+ *
41
+ * @param operationId - Optional operation ID to parse
42
+ * @returns The command name if the operation ID follows the expected format, null otherwise
43
+ */
44
+ export declare function operationIdToCommandName(operationId?: string): string | null;
45
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/aggregate/utils.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,GAAG,EAAE,MAAM,4BAA4B,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErE;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAMzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,iBAAiB,GAAG,IAAI,CAWjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,CAAC,EAAE,GAAG,EAAE,GACX,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,mBAAmB,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAC,CAmBjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS5E"}
@@ -0,0 +1,22 @@
1
+ import { Project } from 'ts-morph';
2
+ import { OpenAPI } from '@ahoo-wang/fetcher-openapi';
3
+ import { GenerateContext, Logger } from './types';
4
+ import { BoundedContextAggregates } from './aggregate';
5
+ export declare abstract class BaseCodeGenerator implements GenerateContext {
6
+ readonly project: Project;
7
+ readonly openAPI: OpenAPI;
8
+ readonly outputDir: string;
9
+ readonly contextAggregates: BoundedContextAggregates;
10
+ readonly logger?: Logger;
11
+ /**
12
+ * Creates a new ClientGenerator instance.
13
+ * @param context - The generation context containing OpenAPI spec and project details
14
+ */
15
+ protected constructor(context: GenerateContext);
16
+ /**
17
+ * Generates code based on the provided context.
18
+ * Subclasses must implement this method to define their specific generation logic.
19
+ */
20
+ abstract generate(): void;
21
+ }
22
+ //# sourceMappingURL=baseCodeGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseCodeGenerator.d.ts","sourceRoot":"","sources":["../src/baseCodeGenerator.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAEvD,8BAAsB,iBAAkB,YAAW,eAAe;IAChE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,wBAAwB,CAAC;IACrD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,SAAS,aAAa,OAAO,EAAE,eAAe;IAQ9C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,IAAI,IAAI;CAC1B"}
package/dist/cli.cjs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ "use strict";const t=require("commander"),i=require("./index.cjs"),c=require("ts-morph");require("yaml");require("fs");require("@ahoo-wang/fetcher");class s{info(e){console.log(`ℹ️ ${e}`)}success(e){console.log(`✅ ${e}`)}error(e){console.error(`❌ ${e}`)}progress(e){console.log(`🔄 ${e}`)}}function a(r){if(!r)return!1;try{const e=new URL(r);return e.protocol==="http:"||e.protocol==="https:"}catch{return r.length>0}}async function p(r){const e=new s;process.on("SIGINT",()=>{e.error("Generation interrupted by user"),process.exit(130)}),a(r.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 o=new c.Project,n={inputPath:r.input,outputDir:r.output,project:o,logger:e};await new i.CodeGenerator(n).generate(),e.success(`Code generation completed successfully! Files generated in: ${r.output}`)}catch(o){e.error(`Error during code generation: ${o}`),process.exit(1)}}t.program.name("fetcher-generator").description("OpenAPI Specification TypeScript code generator for Wow").version("1.0.0");t.program.command("generate").description("Generate TypeScript code from OpenAPI specification").requiredOption("-i, --input <path>","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").option("-v, --verbose","Enable verbose logging").option("--dry-run","Show what would be generated without writing files").action(p);t.program.parse();
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point for the Fetcher OpenAPI code generator.
4
+ * Sets up the commander program with generate command and handles execution.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;GAGG"}
package/dist/cli.js ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ import { program as t } from "commander";
3
+ import { CodeGenerator as i } from "./index.js";
4
+ import { Project as c } from "ts-morph";
5
+ import "yaml";
6
+ import "fs";
7
+ import "@ahoo-wang/fetcher";
8
+ class p {
9
+ info(e) {
10
+ console.log(`ℹ️ ${e}`);
11
+ }
12
+ success(e) {
13
+ console.log(`✅ ${e}`);
14
+ }
15
+ error(e) {
16
+ console.error(`❌ ${e}`);
17
+ }
18
+ progress(e) {
19
+ console.log(`🔄 ${e}`);
20
+ }
21
+ }
22
+ function a(o) {
23
+ if (!o) return !1;
24
+ try {
25
+ const e = new URL(o);
26
+ return e.protocol === "http:" || e.protocol === "https:";
27
+ } catch {
28
+ return o.length > 0;
29
+ }
30
+ }
31
+ async function s(o) {
32
+ const e = new p();
33
+ process.on("SIGINT", () => {
34
+ e.error("Generation interrupted by user"), process.exit(130);
35
+ }), a(o.input) || (e.error("Invalid input: must be a valid file path or HTTP/HTTPS URL"), process.exit(2));
36
+ try {
37
+ e.info("Starting code generation...");
38
+ const r = new c(), n = {
39
+ inputPath: o.input,
40
+ outputDir: o.output,
41
+ project: r,
42
+ logger: e
43
+ };
44
+ await new i(n).generate(), e.success(
45
+ `Code generation completed successfully! Files generated in: ${o.output}`
46
+ );
47
+ } catch (r) {
48
+ e.error(`Error during code generation: ${r}`), process.exit(1);
49
+ }
50
+ }
51
+ t.name("fetcher-generator").description("OpenAPI Specification TypeScript code generator for Wow").version("1.0.0");
52
+ t.command("generate").description("Generate TypeScript code from OpenAPI specification").requiredOption(
53
+ "-i, --input <path>",
54
+ "Input OpenAPI specification file path or URL (http/https)"
55
+ ).option("-o, --output <path>", "Output directory path", "src/generated").option("-c, --config <file>", "Configuration file path").option("-v, --verbose", "Enable verbose logging").option("--dry-run", "Show what would be generated without writing files").action(s);
56
+ t.parse();
@@ -0,0 +1,25 @@
1
+ import { BaseCodeGenerator } from '../baseCodeGenerator';
2
+ import { GenerateContext } from '../types';
3
+ /**
4
+ * Generates TypeScript client classes for aggregates.
5
+ * Creates query clients and command clients based on aggregate definitions.
6
+ */
7
+ export declare class ClientGenerator extends BaseCodeGenerator {
8
+ private readonly queryClientGenerator;
9
+ private readonly commandClientGenerator;
10
+ /**
11
+ * Creates a new ClientGenerator instance.
12
+ * @param context - The generation context containing OpenAPI spec and project details
13
+ */
14
+ constructor(context: GenerateContext);
15
+ /**
16
+ * Generates client classes for all aggregates.
17
+ */
18
+ generate(): void;
19
+ /**
20
+ * Processes a bounded context by creating a file with the context alias constant.
21
+ * @param contextAlias - The alias of the bounded context to process
22
+ */
23
+ processBoundedContext(contextAlias: string): void;
24
+ }
25
+ //# sourceMappingURL=clientGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clientGenerator.d.ts","sourceRoot":"","sources":["../../src/client/clientGenerator.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAGzD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAG3C;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,iBAAiB;IACpD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAuB;IAC5D,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAyB;IAEhE;;;OAGG;gBACS,OAAO,EAAE,eAAe;IAMpC;;OAEG;IACH,QAAQ,IAAI,IAAI;IAahB;;;OAGG;IACH,qBAAqB,CAAC,YAAY,EAAE,MAAM;CAO3C"}
@@ -0,0 +1,38 @@
1
+ import { BaseCodeGenerator } from '../baseCodeGenerator';
2
+ import { GenerateContext } from '../types';
3
+ import { ClassDeclaration, SourceFile } from 'ts-morph';
4
+ import { AggregateDefinition, CommandDefinition } from '../aggregate';
5
+ /**
6
+ * Generates TypeScript command client classes for aggregates.
7
+ * Creates command clients that can send commands to aggregates.
8
+ */
9
+ export declare class CommandClientGenerator extends BaseCodeGenerator {
10
+ private readonly commandEndpointPathsName;
11
+ private readonly defaultCommandClientOptionsName;
12
+ /**
13
+ * Creates a new CommandClientGenerator instance.
14
+ * @param context - The generation context containing OpenAPI spec and project details
15
+ */
16
+ constructor(context: GenerateContext);
17
+ /**
18
+ * Generates command client classes for all aggregates.
19
+ */
20
+ generate(): void;
21
+ /**
22
+ * Processes and generates command client for an aggregate.
23
+ * @param aggregate - The aggregate definition
24
+ */
25
+ processAggregate(aggregate: AggregateDefinition): void;
26
+ processCommandEndpointPaths(clientFile: SourceFile, aggregateDefinition: AggregateDefinition): void;
27
+ getEndpointPath(command: CommandDefinition): string;
28
+ processCommandClient(clientFile: SourceFile, aggregateDefinition: AggregateDefinition, isStream?: boolean): void;
29
+ private methodToDecorator;
30
+ /**
31
+ * Processes and generates a command method for the command client.
32
+ * @param sourceFile - The source file containing the client
33
+ * @param client - The client class declaration
34
+ * @param definition - The command definition
35
+ */
36
+ processCommandMethod(sourceFile: SourceFile, client: ClassDeclaration, definition: CommandDefinition, returnType: string): void;
37
+ }
38
+ //# sourceMappingURL=commandClientGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandClientGenerator.d.ts","sourceRoot":"","sources":["../../src/client/commandClientGenerator.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EACL,gBAAgB,EAIhB,UAAU,EAEX,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAKtE;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,iBAAiB;IAC3D,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA4B;IACrE,OAAO,CAAC,QAAQ,CAAC,+BAA+B,CACb;IAEnC;;;OAGG;gBACS,OAAO,EAAE,eAAe;IAIpC;;OAEG;IACH,QAAQ,IAAI,IAAI;IAiBhB;;;OAGG;IACH,gBAAgB,CAAC,SAAS,EAAE,mBAAmB;IAsD/C,2BAA2B,CACzB,UAAU,EAAE,UAAU,EACtB,mBAAmB,EAAE,mBAAmB;IAa1C,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM;IAInD,oBAAoB,CAClB,UAAU,EAAE,UAAU,EACtB,mBAAmB,EAAE,mBAAmB,EACxC,QAAQ,GAAE,OAAe;IAiD3B,OAAO,CAAC,iBAAiB;IAOzB;;;;;OAKG;IACH,oBAAoB,CAClB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,iBAAiB,EAC7B,UAAU,EAAE,MAAM;CAoDrB"}
@@ -0,0 +1,2 @@
1
+ export * from './clientGenerator';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAaA,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { SourceFile } from 'ts-morph';
2
+ import { GenerateContext } from '../types';
3
+ import { BaseCodeGenerator } from '../baseCodeGenerator';
4
+ import { AggregateDefinition, TagAliasAggregate } from '../aggregate';
5
+ /**
6
+ * Generates TypeScript query client classes for aggregates.
7
+ * Creates query clients that can perform state queries and event streaming.
8
+ */
9
+ export declare class QueryClientGenerator extends BaseCodeGenerator {
10
+ /**
11
+ * Creates a new QueryClientGenerator instance.
12
+ * @param context - The generation context containing OpenAPI spec and project details
13
+ */
14
+ constructor(context: GenerateContext);
15
+ /**
16
+ * Generates query client classes for all aggregates.
17
+ */
18
+ generate(): void;
19
+ /**
20
+ * Creates or retrieves a source file for client generation.
21
+ * @param aggregate - The aggregate metadata
22
+ * @param fileName - The name of the client file
23
+ * @returns The source file for the client
24
+ */
25
+ createClientFilePath(aggregate: TagAliasAggregate, fileName: string): SourceFile;
26
+ /**
27
+ * Processes and generates query client classes for an aggregate.
28
+ * @param aggregate - The aggregate definition
29
+ */
30
+ processQueryClient(aggregate: AggregateDefinition): void;
31
+ }
32
+ //# sourceMappingURL=queryClientGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queryClientGenerator.d.ts","sourceRoot":"","sources":["../../src/client/queryClientGenerator.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,UAAU,EAA2B,MAAM,UAAU,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAKtE;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,iBAAiB;IACzD;;;OAGG;gBACS,OAAO,EAAE,eAAe;IAIpC;;OAEG;IACH,QAAQ,IAAI,IAAI;IAiBhB;;;;;OAKG;IACH,oBAAoB,CAClB,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,EAAE,MAAM,GACf,UAAU;IASb;;;OAGG;IACH,kBAAkB,CAAC,SAAS,EAAE,mBAAmB;CAwDlD"}
@@ -0,0 +1,12 @@
1
+ import { Project, SourceFile } from 'ts-morph';
2
+ import { AggregateDefinition, TagAliasAggregate } from '../aggregate';
3
+ export declare function inferPathSpecType(aggregateDefinition: AggregateDefinition): string;
4
+ export declare function createClientFilePath(project: Project, outputDir: string, aggregate: TagAliasAggregate, fileName: string): SourceFile;
5
+ /**
6
+ * Generates the client class name for an aggregate.
7
+ * @param aggregate - The aggregate metadata
8
+ * @param suffix - The suffix to append to the aggregate name
9
+ * @returns The generated client class name
10
+ */
11
+ export declare function getClientName(aggregate: TagAliasAggregate, suffix: string): string;
12
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/client/utils.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtE,wBAAgB,iBAAiB,CAC/B,mBAAmB,EAAE,mBAAmB,GACvC,MAAM,CAiBR;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,EAAE,MAAM,GACf,UAAU,CAGZ;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,MAAM,GACb,MAAM,CAER"}
package/dist/index.cjs ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("ts-morph"),B=require("yaml"),L=require("fs"),g=require("@ahoo-wang/fetcher"),v=require("@ahoo-wang/fetcher-wow");function y(r){return r.$ref.split("/").pop()}function P(r,e){const n=y(r);return e.schemas?.[n]}function F(r,e){const n=y(r);return e.requestBodies?.[n]}function K(r,e){const n=y(r);return e.parameters?.[n]}function C(r,e){return{key:y(r),schema:P(r,e)}}const I=/[-_\s.]+|(?=[A-Z])/;function T(r){if(r===""||r.length===0)return"";let e;return Array.isArray(r)?e=r.flatMap(n=>n.split(I)):e=r.split(I),e.filter(n=>n.length>0).map(n=>{if(n.length===0)return"";const t=n.charAt(0),o=n.slice(1);return(/[a-zA-Z]/.test(t)?t.toUpperCase():t)+o.toLowerCase()}).join("")}function D(r){const e=T(r);return e.charAt(0).toLowerCase()+e.slice(1)}function U(r){return r.startsWith("http://")||r.startsWith("https://")?Q(r):V(r)}async function Q(r){return await(await fetch(r)).text()}function V(r){return new Promise((e,n)=>{L.readFile(r,"utf-8",(t,o)=>{t?n(t):e(o)})})}async function k(r){const e=await U(r);switch(J(e)){case"json":return JSON.parse(e);case"yaml":return B.parse(e);default:throw new Error(`Unsupported file format: ${r}`)}}function J(r){const e=r.trimStart();if(e.startsWith("{")||e.startsWith("["))return"json";if(e.startsWith("-")||e.startsWith("%YAML"))return"yaml";try{return JSON.parse(e),"json"}catch{if(e.length>0)return"yaml"}throw new Error("Unable to infer file format")}function l(r){return!!(r&&typeof r=="object"&&"$ref"in r)}function z(r){return!r||l(r)||!r.content?void 0:r.content[g.ContentTypeValues.APPLICATION_JSON]?.schema}function H(r){return[{method:"get",operation:r.get},{method:"put",operation:r.put},{method:"post",operation:r.post},{method:"delete",operation:r.delete},{method:"options",operation:r.options},{method:"head",operation:r.head},{method:"patch",operation:r.patch},{method:"trace",operation:r.trace}].filter(({operation:e})=>e!==void 0)}function M(r){return r.responses[200]}function b(r){const e=M(r);return z(e)}function Y(r){return Array.isArray(r.enum)&&r.enum.length>0}function j(r){if(Array.isArray(r))return r.map(e=>j(e)).join(" | ");switch(r){case"string":return"string";case"number":case"integer":return"number";case"boolean":return"boolean";case"null":return"null";default:return"any"}}const Z="types.ts",S="@";function $(r){return g.combineURLs(r.path,Z)}function O(r,e,n){const t=g.combineURLs(e,n),o=r.getSourceFile(t);return o||r.createSourceFile(t,"",{overwrite:!0})}function E(r,e,n){let t=r.getImportDeclaration(o=>o.getModuleSpecifierValue()===e);t||(t=r.addImportDeclaration({moduleSpecifier:e})),n.forEach(o=>{t.getNamedImports().some(s=>s.getName()===o)||t.addNamedImport(o)})}function h(r,e,n){let t=$(n);n.path.startsWith(S)||(t=g.combineURLs(e,t));let o=t;t.startsWith(S)||(o=g.combineURLs(S,t)),E(r,o,[n.name])}function X(r,e,n,t){r.path!==t.path&&h(e,n,t)}function q(r,e){const n=[r,e].filter(t=>t!==void 0&&t.length>0);return n.length>0?n.join(`
2
+ `):void 0}function x(r,e){const n=q(r,e);return n?[n]:[]}function _(r,e,n){const t=q(e,n);t&&r.addJsDoc({description:t})}function ee(r){const e=r.split(".");return e.length!=2||e[0].length===0||e[1].length===0?null:e}function te(r){const e=ee(r.name);return e?{tag:r,contextAlias:e[0],aggregateName:e[1]}:null}function ne(r){const e=r?.map(t=>te(t)).filter(t=>t!==null);if(!e)return new Map;const n=new Map;return e.forEach(t=>{n.set(t.tag.name,{aggregate:t,commands:new Map,events:new Map})}),n}function re(r){if(!r)return null;const e=r.split(".");return e.length!=3?null:e[2]}const oe="#/components/responses/wow.CommandOk",ae="#/components/parameters/wow.id";class se{constructor(e){this.openAPI=e,this.aggregates=ne(e.tags),this.build()}aggregates;build(){for(const[e,n]of Object.entries(this.openAPI.paths)){const t=H(n);for(const o of t)this.commands(e,o),this.state(o.operation),this.events(o.operation),this.fields(o.operation)}}resolve(){const e=new Map;for(const n of this.aggregates.values()){if(!n.state||!n.fields)continue;const t=n.aggregate.contextAlias;let o=e.get(t);o||(o=new Set,e.set(t,o)),o.add(n)}return e}commands(e,n){const t=n.operation;if(t.operationId==="wow.command.send")return;const o=re(t.operationId);if(!o)return;const a=M(t);if(!a||!l(a)||a.$ref!==oe||!t.requestBody)return;const s=t.parameters??[],c=s.filter(u=>l(u)&&u.$ref===ae).at(0),i=s.filter(u=>!l(u)&&u.in==="path");if(c){const u=K(c,this.openAPI.components);i.push(u)}const m=t.requestBody.content[g.ContentTypeValues.APPLICATION_JSON].schema,f=C(m,this.openAPI.components);f.schema.title=f.schema.title||t.summary,f.schema.description=f.schema.description||t.description;const W={name:o,method:n.method,path:e,pathParameters:i,summary:t.summary,description:t.description,schema:f,operation:t};t.tags?.forEach(u=>{const R=this.aggregates.get(u);R&&R.commands.set(o,W)})}state(e){if(!e.operationId?.endsWith(".snapshot_state.single"))return;const n=b(e);if(!l(n))return;const t=C(n,this.openAPI.components);e.tags?.forEach(o=>{const a=this.aggregates.get(o);a&&(a.state=t)})}events(e){if(!this.openAPI.components||!e.operationId?.endsWith(".event.list_query"))return;const n=b(e);if(l(n))return;const t=n?.items;if(!l(t))return;const a=P(t,this.openAPI.components).properties.body.items.anyOf.map(s=>{const c=s.title,i=s.properties.name.const,p=s.properties.body,m=C(p,this.openAPI.components);return m.schema.title=m.schema.title||s.title,{title:c,name:i,schema:m}});e.tags?.forEach(s=>{const c=this.aggregates.get(s);c&&a.forEach(i=>{c.events.set(i.name,i)})})}fields(e){if(!this.openAPI.components||!e.operationId?.endsWith(".snapshot.count"))return;const t=F(e.requestBody,this.openAPI.components).content[g.ContentTypeValues.APPLICATION_JSON].schema,a=P(t,this.openAPI.components).properties?.field,s=C(a,this.openAPI.components);e.tags?.forEach(c=>{const i=this.aggregates.get(c);i&&(i.fields=s)})}}const N="@ahoo-wang/fetcher-wow",ie={"wow.command.CommandResult":"CommandResult","wow.MessageHeaderSqlType":"MessageHeaderSqlType","wow.api.BindingError":"BindingError","wow.api.DefaultErrorInfo":"ErrorInfo","wow.api.RecoverableType":"RecoverableType","wow.api.command.DefaultDeleteAggregate":"DeleteAggregate","wow.api.command.DefaultRecoverAggregate":"RecoverAggregate","wow.api.messaging.FunctionInfoData":"FunctionInfo","wow.api.messaging.FunctionKind":"FunctionKind","wow.api.modeling.AggregateId":"AggregateId","wow.api.query.Condition":"Condition","wow.api.query.ConditionOptions":"ConditionOptions","wow.api.query.ListQuery":"ListQuery","wow.api.query.Operator":"Operator","wow.api.query.PagedQuery":"PagedQuery","wow.api.query.Pagination":"Pagination","wow.api.query.Projection":"Projection","wow.api.query.Sort":"FieldSort","wow.api.query.Sort.Direction":"SortDirection","wow.command.CommandStage":"CommandStage","wow.command.SimpleWaitSignal":"WaitSignal","wow.configuration.Aggregate":"Aggregate","wow.configuration.BoundedContext":"BoundedContext","wow.configuration.WowMetadata":"WowMetadata","wow.modeling.DomainEvent":"DomainEvent","wow.openapi.BatchResult":"BatchResult","wow.messaging.CompensationTarget":"CompensationTarget"};function d(r){if(!r)return{name:"",path:"/"};const e=ie[r];if(e)return{name:e,path:N};const n=r.split(".");let t=-1;for(let i=0;i<n.length;i++)if(n[i]&&/^[A-Z]/.test(n[i])){t=i;break}if(t===-1)return{name:r,path:"/"};const o=n.slice(0,t),a=o.length>0?`/${o.join("/")}`:"/",s=n.slice(t);return{name:T(s),path:a}}class w{project;openAPI;outputDir;contextAggregates;logger;constructor(e){this.project=e.project,this.openAPI=e.openAPI,this.outputDir=e.outputDir,this.contextAggregates=e.contextAggregates,this.logger=e.logger}}class ce extends w{constructor(e){super(e)}getOrCreateSourceFile(e){const n=$(e);return O(this.project,this.outputDir,n)}generate(){const e=this.openAPI.components?.schemas;if(!e){this.logger?.info("No schemas found in OpenAPI specification");return}this.logger?.progress(`Generating models for ${Object.keys(e).length} schemas`),Object.entries(e).forEach(([n,t])=>{n.startsWith("wow.")||(this.logger?.progress(`Processing schema: ${n}`),this.generateKeyedSchema(n,t))}),this.logger?.success("Model generation completed")}generateKeyedSchema(e,n){const t=d(e),o=this.getOrCreateSourceFile(t),a=this.process(t,o,n);return _(a,n.title,n.description),o}process(e,n,t){let o=this.processEnum(e,n,t);return o||(o=this.processObject(e,n,t),o)||(o=this.processUnion(e,n,t),o)?o:this.processTypeAlias(e,n,t)}processEnum(e,n,t){if(Y(t))return n.addEnum({name:e.name,isExported:!0,members:t.enum.filter(o=>typeof o=="string"&&o.length>0).map(o=>({name:o,initializer:`'${o}'`}))})}processObject(e,n,t){if(t.type!=="object"||!t.properties)return;const o={},a=t.required||[];for(const[s,c]of Object.entries(t.properties)){const i=this.resolveType(e,n,c),p=!a.includes(s);o[s]=p?`${i} | undefined`:i}return n.addInterface({name:e.name,isExported:!0,properties:Object.entries(o).map(([s,c])=>({name:s,type:c}))})}processUnion(e,n,t){let o=null;if(t.allOf?o=t.allOf.map(a=>this.resolveType(e,n,a)).join(" & "):t.anyOf?o=t.anyOf.map(a=>this.resolveType(e,n,a)).join(" | "):t.oneOf&&(o=t.oneOf.map(a=>this.resolveType(e,n,a)).join(" | ")),!!o)return n.addTypeAlias({name:e.name,type:o,isExported:!0,docs:x(t.title,t.description)})}processTypeAlias(e,n,t){const o=this.resolveType(e,n,t);return n.addTypeAlias({name:e.name,type:o,isExported:!0,docs:x(t.title,t.description)})}resolveType(e,n,t){if(l(t))return this.resolveReference(e,n,t);if(t.type==="array")return`${t.items?this.resolveType(e,n,t.items):"any"}[]`;if(t.type==="object"&&!t.properties)return"Record<string, any>";let o;return t.type?o=j(t.type):o="any",t.nullable&&(o=`${o} | null`),o}resolveReference(e,n,t){const o=y(t),a=d(o);return X(e,n,this.outputDir,a),a.name}}function pe(r){let e=0,n=0;return r.commands.forEach(t=>{t.path.startsWith(v.ResourceAttributionPathSpec.TENANT)&&(e+=1),t.path.startsWith(v.ResourceAttributionPathSpec.OWNER)&&(n+=1)}),e===0&&n===0?"ResourceAttributionPathSpec.NONE":e>n?"ResourceAttributionPathSpec.TENANT":"ResourceAttributionPathSpec.OWNER"}function G(r,e,n,t){const o=`${n.contextAlias}/${n.aggregateName}/${t}.ts`;return O(r,e,o)}function ue(r,e){return`${T(r.aggregateName)}${e}`}class me extends w{constructor(e){super(e)}generate(){const e=Array.from(this.contextAggregates.values()).flat().length;this.logger?.progress(`Generating query clients for ${e} aggregates`);for(const[,n]of this.contextAggregates)n.forEach(t=>{this.logger?.progress(`Processing query client for aggregate: ${t.aggregate.aggregateName}`),this.processQueryClient(t)});this.logger?.success("Query client generation completed")}createClientFilePath(e,n){return G(this.project,this.outputDir,e,n)}processQueryClient(e){const n=this.createClientFilePath(e.aggregate,"queryClient");n.addImportDeclaration({moduleSpecifier:N,namedImports:["QueryClientFactory","QueryClientOptions","ResourceAttributionPathSpec"]});const t="DEFAULT_QUERY_CLIENT_OPTIONS";n.addVariableStatement({declarationKind:A.VariableDeclarationKind.Const,declarations:[{name:t,type:"QueryClientOptions",initializer:`{
3
+ contextAlias: '${e.aggregate.contextAlias}',
4
+ aggregateName: '${e.aggregate.aggregateName}',
5
+ resourceAttribution: ${pe(e)},
6
+ }`}],isExported:!1});const o=[];for(const p of e.events.values()){const m=d(p.schema.key);h(n,this.outputDir,m),o.push(m)}const a="DOMAIN_EVENT_TYPES";n.addTypeAlias({name:a,type:o.map(p=>p.name).join(" | ")});const s=`${D(e.aggregate.aggregateName)}QueryClientFactory`,c=d(e.state.key),i=d(e.fields.key);h(n,this.outputDir,c),h(n,this.outputDir,i),n.addVariableStatement({declarationKind:A.VariableDeclarationKind.Const,declarations:[{name:s,initializer:`new QueryClientFactory<${c.name}, ${i.name} | string, ${a}>(${t})`}],isExported:!0})}}class le extends w{commandEndpointPathsName="COMMAND_ENDPOINT_PATHS";defaultCommandClientOptionsName="DEFAULT_COMMAND_CLIENT_OPTIONS";constructor(e){super(e)}generate(){const e=Array.from(this.contextAggregates.values()).flat().length;this.logger?.progress(`Generating command clients for ${e} aggregates`);for(const[,n]of this.contextAggregates)n.forEach(t=>{this.logger?.progress(`Processing command client for aggregate: ${t.aggregate.aggregateName}`),this.processAggregate(t)});this.logger?.success("Command client generation completed")}processAggregate(e){const n=G(this.project,this.outputDir,e.aggregate,"commandClient");this.processCommandEndpointPaths(n,e),n.addVariableStatement({declarationKind:A.VariableDeclarationKind.Const,declarations:[{name:this.defaultCommandClientOptionsName,type:"ApiMetadata",initializer:`{
7
+ basePath: '${e.aggregate.contextAlias}'
8
+ }`}],isExported:!1}),n.addImportDeclaration({moduleSpecifier:N,namedImports:["CommandRequest","CommandResult","CommandResultEventStream","DeleteAggregate","RecoverAggregate"],isTypeOnly:!0}),n.addImportDeclaration({moduleSpecifier:"@ahoo-wang/fetcher-eventstream",namedImports:["JsonEventStreamResultExtractor"]}),E(n,"@ahoo-wang/fetcher",["ContentTypeValues"]),E(n,"@ahoo-wang/fetcher-decorator",["type ApiMetadata","type ApiMetadataCapable","api","post","put","del","request","attribute","path","autoGeneratedError"]),this.processCommandClient(n,e),this.processCommandClient(n,e,!0)}processCommandEndpointPaths(e,n){const t=e.addEnum({name:this.commandEndpointPathsName});n.commands.forEach(o=>{t.addMember({name:o.name.toUpperCase(),initializer:`'${o.path}'`})})}getEndpointPath(e){return`${this.commandEndpointPathsName}.${e.name.toUpperCase()}`}processCommandClient(e,n,t=!1){let o="CommandClient",a={name:"api",arguments:[]},s="Promise<CommandResult>";t&&(o="Stream"+o,a={name:"api",arguments:["''",`{
9
+ headers: { Accept: ContentTypeValues.TEXT_EVENT_STREAM },
10
+ resultExtractor: JsonEventStreamResultExtractor,
11
+ }`]},s="Promise<CommandResultEventStream>");const c=ue(n.aggregate,o),i=e.addClass({name:c,isExported:!0,decorators:[a],implements:["ApiMetadataCapable"]});i.addConstructor({parameters:[{name:"apiMetadata",type:"ApiMetadata",scope:A.Scope.Public,isReadonly:!0,initializer:`${this.defaultCommandClientOptionsName}`}]}),n.commands.forEach(p=>{this.processCommandMethod(e,i,p,s)})}methodToDecorator(e){return e==="delete"?"del":e}processCommandMethod(e,n,t,o){const a=d(t.schema.key);h(e,this.outputDir,a);const s=t.pathParameters.map(i=>({name:i.name,type:"string",decorators:[{name:"path",arguments:[`'${i.name}'`]}]}));s.push({name:"commandRequest",type:`CommandRequest<${a.name}>`,decorators:[{name:"request",arguments:[]}]}),s.push({name:"attributes",type:"Record<string, any>",decorators:[{name:"attribute",arguments:[]}]});const c=n.addMethod({name:D(t.name),decorators:[{name:this.methodToDecorator(t.method),arguments:[`${this.getEndpointPath(t)}`]}],parameters:s,returnType:o,statements:[`throw autoGeneratedError(${s.map(i=>i.name).join(",")});`]});_(c,t.summary,t.description)}}class ge extends w{queryClientGenerator;commandClientGenerator;constructor(e){super(e),this.queryClientGenerator=new me(e),this.commandClientGenerator=new le(e)}generate(){this.logger?.progress(`Generating clients for ${this.contextAggregates.size} bounded contexts`);for(const[e]of this.contextAggregates)this.logger?.progress(`Processing bounded context: ${e}`),this.processBoundedContext(e);this.queryClientGenerator.generate(),this.commandClientGenerator.generate(),this.logger?.success("Client generation completed")}processBoundedContext(e){const n=`${e}/boundedContext.ts`;O(this.project,this.outputDir,n).addStatements(`export const BOUNDED_CONTEXT_ALIAS = '${e}';`)}}class de{constructor(e){this.options=e,this.project=e.project}project;async generate(){const e=await k(this.options.inputPath),t=new se(e).resolve(),o={openAPI:e,project:this.project,outputDir:this.options.outputDir,contextAggregates:t,logger:this.options.logger};new ce(o).generate(),new ge(o).generate(),this.project.getSourceFiles().forEach(c=>{c.formatText(),c.organizeImports(),c.fixMissingImports()}),await this.project.save()}}exports.CodeGenerator=de;
@@ -0,0 +1,21 @@
1
+ import { GeneratorOptions } from './types';
2
+ /**
3
+ * Main code generator class that orchestrates the generation of TypeScript code from OpenAPI specifications.
4
+ * Handles model generation, client generation, and project formatting.
5
+ */
6
+ export declare class CodeGenerator {
7
+ private readonly options;
8
+ private readonly project;
9
+ /**
10
+ * Creates a new CodeGenerator instance.
11
+ * @param options - Configuration options for code generation
12
+ */
13
+ constructor(options: GeneratorOptions);
14
+ /**
15
+ * Generates TypeScript code from the OpenAPI specification.
16
+ * Parses the OpenAPI spec, resolves aggregates, generates models and clients,
17
+ * and formats the output files.
18
+ */
19
+ generate(): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAmB,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAM5D;;;GAGG;AACH,qBAAa,aAAa;IAOZ,OAAO,CAAC,QAAQ,CAAC,OAAO;IANpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAElC;;;OAGG;gBAC0B,OAAO,EAAE,gBAAgB;IAItD;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAsBhC"}