@ahoo-wang/wow-generator 9.2.0-rc.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 (116) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +177 -0
  3. package/README.zh-CN.md +149 -0
  4. package/dist/analysis/aggregates.d.cts +21 -0
  5. package/dist/analysis/aggregates.d.ts +21 -0
  6. package/dist/analysis/analyze.d.cts +18 -0
  7. package/dist/analysis/analyze.d.ts +18 -0
  8. package/dist/analysis/apiClients.d.cts +14 -0
  9. package/dist/analysis/apiClients.d.ts +14 -0
  10. package/dist/analysis/clientNames.d.cts +55 -0
  11. package/dist/analysis/clientNames.d.ts +55 -0
  12. package/dist/analysis/model.d.cts +212 -0
  13. package/dist/analysis/model.d.ts +212 -0
  14. package/dist/analysis/modelInfo.d.cts +23 -0
  15. package/dist/analysis/modelInfo.d.ts +23 -0
  16. package/dist/analysis/models.d.cts +17 -0
  17. package/dist/analysis/models.d.ts +17 -0
  18. package/dist/api/configuration.d.cts +30 -0
  19. package/dist/api/configuration.d.ts +30 -0
  20. package/dist/api/errors.d.cts +41 -0
  21. package/dist/api/errors.d.ts +41 -0
  22. package/dist/api/logger.d.cts +61 -0
  23. package/dist/api/logger.d.ts +61 -0
  24. package/dist/api/options.d.cts +47 -0
  25. package/dist/api/options.d.ts +47 -0
  26. package/dist/cli/program.d.cts +35 -0
  27. package/dist/cli/program.d.ts +35 -0
  28. package/dist/cli/runGenerate.d.cts +65 -0
  29. package/dist/cli/runGenerate.d.ts +65 -0
  30. package/dist/cli.cjs +3 -0
  31. package/dist/cli.cjs.map +1 -0
  32. package/dist/cli.d.cts +6 -0
  33. package/dist/cli.d.ts +6 -0
  34. package/dist/cli.js +98 -0
  35. package/dist/cli.js.map +1 -0
  36. package/dist/codeGenerator-DpDTDC4o.cjs +23 -0
  37. package/dist/codeGenerator-DpDTDC4o.cjs.map +1 -0
  38. package/dist/codeGenerator-kyY9eLML.js +2583 -0
  39. package/dist/codeGenerator-kyY9eLML.js.map +1 -0
  40. package/dist/emit/importRegistry.d.cts +50 -0
  41. package/dist/emit/importRegistry.d.ts +50 -0
  42. package/dist/emit/imports.d.cts +49 -0
  43. package/dist/emit/imports.d.ts +49 -0
  44. package/dist/emit/jsdoc.d.cts +38 -0
  45. package/dist/emit/jsdoc.d.ts +38 -0
  46. package/dist/emit/moduleBuilder.d.cts +80 -0
  47. package/dist/emit/moduleBuilder.d.ts +80 -0
  48. package/dist/emitters/apiClients.d.cts +10 -0
  49. package/dist/emitters/apiClients.d.ts +10 -0
  50. package/dist/emitters/commandClients.d.cts +14 -0
  51. package/dist/emitters/commandClients.d.ts +14 -0
  52. package/dist/emitters/decorators.d.cts +83 -0
  53. package/dist/emitters/decorators.d.ts +83 -0
  54. package/dist/emitters/emit.d.cts +15 -0
  55. package/dist/emitters/emit.d.ts +15 -0
  56. package/dist/emitters/indexFiles.d.cts +12 -0
  57. package/dist/emitters/indexFiles.d.ts +12 -0
  58. package/dist/emitters/models.d.cts +77 -0
  59. package/dist/emitters/models.d.ts +77 -0
  60. package/dist/emitters/queryClients.d.cts +11 -0
  61. package/dist/emitters/queryClients.d.ts +11 -0
  62. package/dist/emitters/target.d.cts +14 -0
  63. package/dist/emitters/target.d.ts +14 -0
  64. package/dist/finalize/finalize.d.cts +16 -0
  65. package/dist/finalize/finalize.d.ts +16 -0
  66. package/dist/finalize/typeOnlyImports.d.cts +13 -0
  67. package/dist/finalize/typeOnlyImports.d.ts +13 -0
  68. package/dist/finalize/verification.d.cts +14 -0
  69. package/dist/finalize/verification.d.ts +14 -0
  70. package/dist/index.cjs +1 -0
  71. package/dist/index.d.cts +8 -0
  72. package/dist/index.d.ts +8 -0
  73. package/dist/index.js +2 -0
  74. package/dist/input/configuration.d.cts +87 -0
  75. package/dist/input/configuration.d.ts +87 -0
  76. package/dist/input/parsers.d.cts +39 -0
  77. package/dist/input/parsers.d.ts +39 -0
  78. package/dist/input/resources.d.cts +36 -0
  79. package/dist/input/resources.d.ts +36 -0
  80. package/dist/naming/modelInfo.d.cts +10 -0
  81. package/dist/naming/modelInfo.d.ts +10 -0
  82. package/dist/naming/naming.d.cts +102 -0
  83. package/dist/naming/naming.d.ts +102 -0
  84. package/dist/naming/order.d.cts +2 -0
  85. package/dist/naming/order.d.ts +2 -0
  86. package/dist/naming/paths.d.cts +27 -0
  87. package/dist/naming/paths.d.ts +27 -0
  88. package/dist/openapi/components.d.cts +55 -0
  89. package/dist/openapi/components.d.ts +55 -0
  90. package/dist/openapi/document.d.cts +25 -0
  91. package/dist/openapi/document.d.ts +25 -0
  92. package/dist/openapi/operations.d.cts +78 -0
  93. package/dist/openapi/operations.d.ts +78 -0
  94. package/dist/openapi/references.d.cts +28 -0
  95. package/dist/openapi/references.d.ts +28 -0
  96. package/dist/openapi/responses.d.cts +44 -0
  97. package/dist/openapi/responses.d.ts +44 -0
  98. package/dist/openapi/schemas.d.cts +112 -0
  99. package/dist/openapi/schemas.d.ts +112 -0
  100. package/dist/output/outputStore.d.cts +92 -0
  101. package/dist/output/outputStore.d.ts +92 -0
  102. package/dist/pipeline/codeGenerator.d.cts +61 -0
  103. package/dist/pipeline/codeGenerator.d.ts +61 -0
  104. package/dist/pipeline/seams.d.cts +29 -0
  105. package/dist/pipeline/seams.d.ts +29 -0
  106. package/dist/types/typeResolver.d.cts +124 -0
  107. package/dist/types/typeResolver.d.ts +124 -0
  108. package/dist/version.d.cts +2 -0
  109. package/dist/version.d.ts +2 -0
  110. package/dist/wow/conventions.d.cts +154 -0
  111. package/dist/wow/conventions.d.ts +154 -0
  112. package/dist/wow/model.d.cts +116 -0
  113. package/dist/wow/model.d.ts +116 -0
  114. package/dist/wow/resolveWowModel.d.cts +21 -0
  115. package/dist/wow/resolveWowModel.d.ts +21 -0
  116. package/package.json +108 -0
package/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [2024] [ahoo wang <ahoowang@qq.com>]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # `@ahoo-wang/wow-generator`
2
+
3
+ Generate TypeScript models, Fetcher decorator clients, and Wow clients from a
4
+ local or remote OpenAPI document.
5
+
6
+ ## Install and run
7
+
8
+ ```bash
9
+ pnpm add @ahoo-wang/fetcher @ahoo-wang/fetcher-decorator \
10
+ @ahoo-wang/fetcher-eventstream @ahoo-wang/wow-client
11
+ pnpm add -D @ahoo-wang/wow-generator typescript
12
+ pnpm exec wow-generator generate \
13
+ --input ./openapi.yaml \
14
+ --output ./src/generated \
15
+ --ts-config-file-path ./tsconfig.json
16
+ ```
17
+
18
+ The version follows Wow, so a minor release may contain breaking changes: keep the Wow packages on one minor with `save-prefix=~` or `--save-exact`, as [version ranges](https://wow.ahoo.me/guide/typescript/compatibility#version-ranges) explains.
19
+
20
+ The first line installs what the generated code imports at run time, the
21
+ second the generator and TypeScript. The generator's peers (`fetcher`,
22
+ `fetcher-decorator`, `fetcher-eventstream`, `wow-client`) are the runtime
23
+ packages of the first line; `wow-client` has to be on the generator's minor
24
+ version. Node 22.12 or later is required, and TypeScript 6 or later: CI tests
25
+ 6.0 through the latest 7.x. The command
26
+ used to be `fetcher-generator`; that name stays as an alias until v10.
27
+
28
+ Generated clients are decorator classes, so the project that compiles them
29
+ needs `"experimentalDecorators": true` in its `tsconfig.json`. Released with
30
+ Wow 9.2.0, from the same tag and with the same version.
31
+
32
+ A successful run prints its warnings, if any, and one summary line:
33
+
34
+ ```text
35
+ Generated 12 files into src/generated with /work/app/wow-generator.config.json, 1 warning
36
+ ```
37
+
38
+ ## Options and exit codes
39
+
40
+ | Option | Meaning |
41
+ | ---------------------------------- | ------------------------------------------------------------------------- |
42
+ | `-i, --input <file>` | OpenAPI 3.x document: a file path or an http(s) URL (required) |
43
+ | `-o, --output <path>` | Output directory, `src/generated` by default |
44
+ | `-c, --config <file>` | Configuration file, `./wow-generator.config.json` by default |
45
+ | `-t, --ts-config-file-path <file>` | The project's `tsconfig.json` |
46
+ | `-H, --header <header>` | `Name: value` header for an http(s) input or configuration; repeatable |
47
+ | `--timeout <ms>` | Abandon an http(s) fetch after this many milliseconds, 30000 by default |
48
+ | `--schema-docs <mode>` | `summary` (default) or `full`, which also embeds each model's JSON schema |
49
+ | `--strict` | Exit with code 4 when the run logged a warning |
50
+ | `--verbose` | Log every step with timestamps, and the stack trace of a failure |
51
+ | `--quiet` | Log only warnings and errors |
52
+
53
+ An http(s) input fails on a response outside 2xx or when the timeout expires.
54
+ Swagger 2.0 documents are refused; convert them to OpenAPI 3 first.
55
+
56
+ | Exit code | Meaning |
57
+ | --------- | -------------------------------------------------------------------------------------------------------------------------- |
58
+ | 0 | Generated; or `--help`, `help` or `--version` printed |
59
+ | 1 | Internal error; rerun with `--verbose` for the stack trace |
60
+ | 2 | Input or usage: unreadable, unfetchable, not OpenAPI 3.x; or an invalid or missing option, an unknown option or command |
61
+ | 3 | Configuration: cannot be read, parsed or validated |
62
+ | 4 | Specification: the document cannot be generated, or `--strict` and the run had warnings |
63
+ | 5 | Output: the manifest cannot be parsed or a newer generator wrote it, a path escapes the output, or a write or delete fails |
64
+ | 130 | Interrupted (Ctrl-C) |
65
+
66
+ A failure prints one line naming what failed; `--verbose` adds the cause.
67
+
68
+ ## Configuration
69
+
70
+ ```json
71
+ {
72
+ "apiClients": {
73
+ "Catalog": {
74
+ "ignorePathParameters": ["tenantId", "ownerId"],
75
+ "methodNames": { "getCatalog_1": "getArchivedCatalog" }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ The default config path is `./wow-generator.config.json`, resolved against
82
+ the working directory the CLI runs in. Only that default path is optional: when
83
+ no file is there the CLI generates with defaults. A
84
+ `./fetcher-generator.config.json`, the name the file had before, is still read
85
+ when the new name is absent, with a deprecation warning; rename it, as v10 no
86
+ longer reads it. A path passed to
87
+ `--config` has to exist, and a configuration that cannot be read, cannot be
88
+ parsed, or declares an option with the wrong shape fails the run rather than
89
+ degrading to defaults. Options the generator does not read - a misspelled
90
+ `apiClient`, say - are warned about by name and ignored.
91
+
92
+ The summary line names the absolute path of the configuration it read, so a
93
+ run answers "did my configuration take effect?" on its own. `--verbose` also
94
+ prints the settings it resolved to:
95
+
96
+ ```text
97
+ [10:42:07] Configuration loaded from /work/app/wow-generator.config.json: apiClients=Catalog
98
+ ```
99
+
100
+ If the summary names no configuration, the file never reached the generator -
101
+ check which directory the CLI ran in.
102
+
103
+ The generator records the files it wrote in `.wow-generator.json` at the root
104
+ of the output directory, so a later run removes the files it no longer
105
+ generates. Commit it with the output. A `.fetcher-generator.json` left by an
106
+ older version is read once and replaced.
107
+
108
+ `ignorePathParameters` defaults to `['tenantId', 'ownerId']` only in a Wow
109
+ document (one with `x-wow-context-alias` or aggregate routes); other documents
110
+ keep those path parameters. `methodNames` names the method of an operationId.
111
+
112
+ ## Generated code
113
+
114
+ - Every file starts with `// Code generated by wow-generator. DO NOT EDIT.`.
115
+ Relative imports end in `.js`, so the output compiles under `NodeNext` as
116
+ well as `bundler`, and it is the same wherever it is written.
117
+ - A method is named by `methodNames`, else `x-fetcher-method`, else the last
118
+ segment of the operationId camel-cased (`example.cart.add_cart_item` →
119
+ `addCartItem`). Adding an operation never renames a method; two operations
120
+ of one client with the same name fail with exit code 4.
121
+ - Command clients, and API clients of a document with `x-wow-context-alias`,
122
+ merge the constructor's `apiMetadata` over their defaults:
123
+ `new CartCommandClient({ fetcher })` keeps the bounded-context base path.
124
+ Pass `basePath: ''` (query factories: `contextAlias: ''`) to reach a service
125
+ directly.
126
+ - Query client factories type their fields as `` `${CartAggregatedFields}` ``;
127
+ annotate a query as ``ListQuery<`${CartAggregatedFields}`>``.
128
+ - `--schema-docs full` embeds each model's JSON schema in its doc comment;
129
+ the default is a summary.
130
+
131
+ ## Property optionality
132
+
133
+ Every property a schema declares is generated as required. A statically typed
134
+ service has no absent `int` or `boolean` to hand back, so a model full of `?`
135
+ describes its exporter rather than its wire format - exporters routinely drop
136
+ properties that carry a default value from `required`, and the resulting nulls
137
+ checks are noise.
138
+
139
+ Optionality that the document genuinely means is carried where it belongs:
140
+
141
+ - **Null** stays in the type. A nullable property generates `T | null`, so a
142
+ value that may be absent is still impossible to forget.
143
+ - **Commands** keep their declared optionality at the command type. A command
144
+ client wraps its body in `PartialBy<Command, 'field' | ...>` built from the
145
+ document's `required`, so a caller may still omit what the API says is
146
+ optional - `AddCartItemCommand = CommandBody<PartialBy<AddCartItem,
147
+ 'quantity'>>`. Requiring the model never narrows what a client may send.
148
+ - **API client bodies** are wrapped the same way: a JSON request body is
149
+ `PartialBy<Model, 'field' | ...>` for the properties its schema leaves out of
150
+ `required`.
151
+
152
+ One consequence is worth knowing: a non-nullable property that references its
153
+ own schema has no finite literal, since every level needs the next. A recursive
154
+ model that terminates declares its link nullable, which generates `T | null` and
155
+ constructs fine.
156
+
157
+ ## Core capabilities
158
+
159
+ - Local JSON/YAML and HTTP(S) OpenAPI input.
160
+ - TypeScript models and decorator API clients grouped by tag, with typed path,
161
+ query and header parameters and request bodies, required ones first.
162
+ - Wow bounded-context, command, snapshot, event, and query discovery.
163
+ - Recursive `index.ts` generation and ts-morph formatting.
164
+ - Programmatic `CodeGenerator` API with injectable logging, returning the files
165
+ written and the warning count.
166
+
167
+ Regenerate after every contract change and compile the result before publishing.
168
+
169
+ ## Documentation
170
+
171
+ - [Quick start: generate a client from a Wow service and call it](https://wow.ahoo.me/guide/typescript/quick-start)
172
+ - [Regenerate in CI](https://wow.ahoo.me/guide/typescript/regenerate-in-ci)
173
+ - [Generate a client from any OpenAPI document](https://wow.ahoo.me/guide/typescript/generated-client)
174
+ - [wow-generator reference](https://wow.ahoo.me/reference/typescript/wow-generator/)
175
+ - [Compatibility and versions](https://wow.ahoo.me/guide/typescript/compatibility)
176
+
177
+ [中文](./README.zh-CN.md) · [License](https://github.com/Ahoo-Wang/Wow/blob/main/LICENSE)
@@ -0,0 +1,149 @@
1
+ # `@ahoo-wang/wow-generator`
2
+
3
+ 从本地或远程 OpenAPI 文档生成 TypeScript 模型、Fetcher Decorator 客户端与 Wow
4
+ 客户端。
5
+
6
+ ## 安装与运行
7
+
8
+ ```bash
9
+ pnpm add @ahoo-wang/fetcher @ahoo-wang/fetcher-decorator \
10
+ @ahoo-wang/fetcher-eventstream @ahoo-wang/wow-client
11
+ pnpm add -D @ahoo-wang/wow-generator typescript
12
+ pnpm exec wow-generator generate \
13
+ --input ./openapi.yaml \
14
+ --output ./src/generated \
15
+ --ts-config-file-path ./tsconfig.json
16
+ ```
17
+
18
+ 版本号跟随 Wow,次版本可能带有破坏性改动:用 `save-prefix=~` 或 `--save-exact` 让 Wow 包停在同一个次版本上,见[版本范围](https://wow.ahoo.me/zh/guide/typescript/compatibility#版本范围)。
19
+
20
+ 第一行安装生成代码运行时导入的包,第二行安装生成器和 TypeScript。生成器的 peer(`fetcher`、
21
+ `fetcher-decorator`、`fetcher-eventstream`、`wow-client`)就是第一行的运行时包;`wow-client`
22
+ 须与生成器的次版本一致。需要 Node 22.12 或更高版本,以及 TypeScript 6 或更高版本:CI 测试
23
+ 6.0 到最新的 7.x。命令原名
24
+ `fetcher-generator`,这个名字作为别名保留到 v10。
25
+
26
+ 生成的客户端是装饰器类,编译它们的项目需要在 `tsconfig.json` 中设置
27
+ `"experimentalDecorators": true`。随 Wow 9.2.0 发布,与 Wow 同一个 tag、同一个版本号。
28
+
29
+ 生成成功时先输出警告(如有),最后输出一行摘要:
30
+
31
+ ```text
32
+ Generated 12 files into src/generated with /work/app/wow-generator.config.json, 1 warning
33
+ ```
34
+
35
+ ## 选项与退出码
36
+
37
+ | 选项 | 含义 |
38
+ | ---------------------------------- | ------------------------------------------------------------ |
39
+ | `-i, --input <file>` | OpenAPI 3.x 文档:文件路径或 http(s) URL(必填) |
40
+ | `-o, --output <path>` | 输出目录,默认 `src/generated` |
41
+ | `-c, --config <file>` | 配置文件,默认 `./wow-generator.config.json` |
42
+ | `-t, --ts-config-file-path <file>` | 项目的 `tsconfig.json` |
43
+ | `-H, --header <header>` | 读取 http(s) 输入或配置时发送的 `Name: value` 请求头,可重复 |
44
+ | `--timeout <ms>` | http(s) 请求超过这个毫秒数即放弃,默认 30000 |
45
+ | `--schema-docs <mode>` | `summary`(默认)或 `full`,后者还嵌入每个模型的 JSON schema |
46
+ | `--strict` | 本次运行有警告时以退出码 4 结束 |
47
+ | `--verbose` | 输出每一步(带时间戳),失败时输出堆栈 |
48
+ | `--quiet` | 只输出警告和错误 |
49
+
50
+ http(s) 输入的响应不是 2xx 或超时都会失败。Swagger 2.0 文档会被拒绝,请先转换为 OpenAPI 3。
51
+
52
+ | 退出码 | 含义 |
53
+ | ------ | ---------------------------------------------------------------------------------------- |
54
+ | 0 | 生成成功;或已输出 `--help`、`help`、`--version` |
55
+ | 1 | 内部错误;加 `--verbose` 重跑可看到堆栈 |
56
+ | 2 | 输入或用法:读不到、取不到、不是 OpenAPI 3.x;或选项值无效、缺少必填选项、未知选项或命令 |
57
+ | 3 | 配置:读不到、解析不了或校验不通过 |
58
+ | 4 | 规范:文档无法生成代码,或开启 `--strict` 且本次运行有警告 |
59
+ | 5 | 输出:清单解析不了或由更新版本的生成器写出、路径越出输出目录,或写入、删除文件失败 |
60
+ | 130 | 被中断(Ctrl-C) |
61
+
62
+ 失败时只输出一行,说明哪里失败;`--verbose` 会附上原因。
63
+
64
+ ## 配置
65
+
66
+ ```json
67
+ {
68
+ "apiClients": {
69
+ "Catalog": {
70
+ "ignorePathParameters": ["tenantId", "ownerId"],
71
+ "methodNames": { "getCatalog_1": "getArchivedCatalog" }
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ 默认配置路径为 `./wow-generator.config.json`,相对 CLI 的运行目录解析。只有这个默认路径是
78
+ 可选的:该位置没有文件时,CLI 按默认值生成。新文件名不存在时,仍会读取旧名
79
+ `./fetcher-generator.config.json` 并给出弃用警告;请改名,v10 起不再读取旧名。`--config` 指定的路径必须
80
+ 存在;配置读不到、解析不了,或某个选项的形态不对,都会让本次生成失败,而不是退回默认值。
81
+ 生成器不认识的选项——例如拼错的 `apiClient`——会被逐个点名警告并忽略。
82
+
83
+ 摘要行会写出实际读取的配置的绝对路径,一次运行就能回答"我的配置生效了吗";`--verbose` 还会
84
+ 写出解析出的设置:
85
+
86
+ ```text
87
+ [10:42:07] Configuration loaded from /work/app/wow-generator.config.json: apiClients=Catalog
88
+ ```
89
+
90
+ 如果摘要里没有配置路径,说明配置文件根本没送到生成器——请检查 CLI 的运行目录。
91
+
92
+ 生成器把写出的文件记录在输出目录根下的 `.wow-generator.json` 中,之后的运行据此删除不再生成的
93
+ 文件。请把它和输出一起提交。旧版本留下的 `.fetcher-generator.json` 会被读取一次并替换。
94
+
95
+ `ignorePathParameters` 只在 Wow 文档(带 `x-wow-context-alias` 或聚合路由)中默认为
96
+ `['tenantId', 'ownerId']`;其他文档保留这两个路径参数。`methodNames` 为 operationId 指定方法名。
97
+
98
+ ## 生成的代码
99
+
100
+ - 每个文件以 `// Code generated by wow-generator. DO NOT EDIT.` 开头。相对导入以 `.js` 结尾,
101
+ 所以输出在 `NodeNext` 和 `bundler` 下都能编译,且写到哪里都一样。
102
+ - 方法名依次取 `methodNames`、`x-fetcher-method`,否则取 operationId 最后一段的 camelCase
103
+ (`example.cart.add_cart_item` → `addCartItem`)。新增操作不会让已有方法改名;同一客户端的两个
104
+ 操作同名时以退出码 4 失败。
105
+ - 命令客户端,以及文档带 `x-wow-context-alias` 时的 API 客户端,把构造器的 `apiMetadata` 合并到
106
+ 默认值之上:`new CartCommandClient({ fetcher })` 保留限界上下文的基础路径。直接访问服务时传
107
+ `basePath: ''`(查询客户端工厂传 `contextAlias: ''`)。
108
+ - 查询客户端工厂的字段类型为 `` `${CartAggregatedFields}` ``;标注查询时写
109
+ ``ListQuery<`${CartAggregatedFields}`>``。
110
+ - `--schema-docs full` 会在每个模型的文档注释中嵌入其 JSON schema;默认只含摘要。
111
+
112
+ ## 属性可选性
113
+
114
+ schema 声明的每个属性都生成为必填。强类型后端根本没有"缺席的 `int`/`boolean`"可以返回,
115
+ 满屏 `?` 的模型描述的是导出器而不是线上格式——导出器常把带默认值的属性排除在 `required`
116
+ 之外,由此产生的空值判断纯属噪音。
117
+
118
+ 文档真正想表达的可选性,则由各自合适的位置承载:
119
+
120
+ - **可空**留在类型里。可空属性生成 `T | null`,可能缺席的值依然不可能被忘记。
121
+ - **命令**在命令类型上保留声明的可选性。命令客户端会依据文档的 `required` 把请求体包成
122
+ `PartialBy<Command, 'field' | ...>`,调用方仍可省略 API 允许省略的字段——
123
+ `AddCartItemCommand = CommandBody<PartialBy<AddCartItem, 'quantity'>>`。把模型改成必填
124
+ 绝不会收紧客户端能发的内容。
125
+ - **API 客户端请求体**也同样处理:JSON 请求体生成为 `PartialBy<Model, 'field' | ...>`,覆盖 schema
126
+ 未列入 `required` 的属性。
127
+
128
+ 有一点值得知道:引用自身 schema 的非空属性没有有限字面量,因为每一层都还要下一层。能终止
129
+ 的递归模型会把这条链声明为可空,生成 `T | null`,构造起来毫无问题。
130
+
131
+ ## 核心能力
132
+
133
+ - 本地 JSON/YAML 与 HTTP(S) OpenAPI 输入。
134
+ - 按 tag 分组的 TypeScript 模型与 Decorator API 客户端,path、query、header 参数和请求体都带类型,必填的在前。
135
+ - Wow bounded-context、命令、快照、事件与查询发现。
136
+ - 递归生成 `index.ts`,并通过 ts-morph 格式化。
137
+ - 支持注入日志的编程式 `CodeGenerator` API,返回写出的文件与警告数。
138
+
139
+ 每次契约变更后重新生成,并在发布前编译结果。
140
+
141
+ ## 文档
142
+
143
+ - [快速开始:从 Wow 服务生成客户端并调用](https://wow.ahoo.me/zh/guide/typescript/quick-start)
144
+ - [在 CI 中重新生成](https://wow.ahoo.me/zh/guide/typescript/regenerate-in-ci)
145
+ - [从任意 OpenAPI 文档生成客户端](https://wow.ahoo.me/zh/guide/typescript/generated-client)
146
+ - [wow-generator 参考](https://wow.ahoo.me/zh/reference/typescript/wow-generator/)
147
+ - [兼容性与版本](https://wow.ahoo.me/zh/guide/typescript/compatibility)
148
+
149
+ [English](./README.md) · [许可证](https://github.com/Ahoo-Wang/Wow/blob/main/LICENSE)
@@ -0,0 +1,21 @@
1
+ import { OpenApiDocument } from '../openapi/document.cjs';
2
+ import { WowModel } from '../wow/model.cjs';
3
+ import { AggregateModel, ModelDeclaration } from './model.cjs';
4
+ /**
5
+ * The command and query clients of every aggregate that has state and query
6
+ * fields, by bounded context in the order the Wow metadata lists them.
7
+ */
8
+ export declare function analyzeAggregates(document: OpenApiDocument, wow: WowModel): AggregateModel[];
9
+ /**
10
+ * Fails when a command's type would take a name its module or its package
11
+ * already holds: the alias of another command, a command body the client
12
+ * imports, or a model of the aggregate's package, which the package's index
13
+ * exports beside it. `Foo` and `FooCommand` as two commands of one aggregate
14
+ * do this: `Foo`'s alias is `FooCommand`, the other's body.
15
+ *
16
+ * @param aggregates - The aggregates, as analysed
17
+ * @param models - The models of the document
18
+ * @throws GeneratorError naming the command, the model it collides with and
19
+ * the schemas to rename
20
+ */
21
+ export declare function assertCommandTypeNamesFree(aggregates: readonly AggregateModel[], models: readonly ModelDeclaration[]): void;
@@ -0,0 +1,21 @@
1
+ import { OpenApiDocument } from '../openapi/document.js';
2
+ import { WowModel } from '../wow/model.js';
3
+ import { AggregateModel, ModelDeclaration } from './model.js';
4
+ /**
5
+ * The command and query clients of every aggregate that has state and query
6
+ * fields, by bounded context in the order the Wow metadata lists them.
7
+ */
8
+ export declare function analyzeAggregates(document: OpenApiDocument, wow: WowModel): AggregateModel[];
9
+ /**
10
+ * Fails when a command's type would take a name its module or its package
11
+ * already holds: the alias of another command, a command body the client
12
+ * imports, or a model of the aggregate's package, which the package's index
13
+ * exports beside it. `Foo` and `FooCommand` as two commands of one aggregate
14
+ * do this: `Foo`'s alias is `FooCommand`, the other's body.
15
+ *
16
+ * @param aggregates - The aggregates, as analysed
17
+ * @param models - The models of the document
18
+ * @throws GeneratorError naming the command, the model it collides with and
19
+ * the schemas to rename
20
+ */
21
+ export declare function assertCommandTypeNamesFree(aggregates: readonly AggregateModel[], models: readonly ModelDeclaration[]): void;
@@ -0,0 +1,18 @@
1
+ import { GeneratorConfiguration } from '../api/configuration.cjs';
2
+ import { OpenApiDocument } from '../openapi/document.cjs';
3
+ import { WowModel } from '../wow/model.cjs';
4
+ import { Analysis } from './model.cjs';
5
+ /**
6
+ * Decides what a document generates: its bounded contexts, models, command
7
+ * and query clients and API clients. A pure function of the document, its
8
+ * Wow model and the configuration; nothing is written and nothing logged.
9
+ *
10
+ * @param document - The document, left unchanged
11
+ * @param wow - Its Wow model
12
+ * @param config - The generator configuration
13
+ * @returns The generation model, and the warnings for what it leaves out
14
+ * @throws GeneratorError when the document asks for code that cannot
15
+ * compile: two schemas of one model, a command type named like a model,
16
+ * two operations of one method
17
+ */
18
+ export declare function analyze(document: OpenApiDocument, wow: WowModel, config: GeneratorConfiguration): Analysis;
@@ -0,0 +1,18 @@
1
+ import { GeneratorConfiguration } from '../api/configuration.js';
2
+ import { OpenApiDocument } from '../openapi/document.js';
3
+ import { WowModel } from '../wow/model.js';
4
+ import { Analysis } from './model.js';
5
+ /**
6
+ * Decides what a document generates: its bounded contexts, models, command
7
+ * and query clients and API clients. A pure function of the document, its
8
+ * Wow model and the configuration; nothing is written and nothing logged.
9
+ *
10
+ * @param document - The document, left unchanged
11
+ * @param wow - Its Wow model
12
+ * @param config - The generator configuration
13
+ * @returns The generation model, and the warnings for what it leaves out
14
+ * @throws GeneratorError when the document asks for code that cannot
15
+ * compile: two schemas of one model, a command type named like a model,
16
+ * two operations of one method
17
+ */
18
+ export declare function analyze(document: OpenApiDocument, wow: WowModel, config: GeneratorConfiguration): Analysis;
@@ -0,0 +1,14 @@
1
+ import { GeneratorConfiguration } from '../api/configuration.cjs';
2
+ import { OpenApiDocument } from '../openapi/document.cjs';
3
+ import { WowModel } from '../wow/model.cjs';
4
+ import { ApiClientModel } from './model.cjs';
5
+ /**
6
+ * The API clients of a document: one per tag that is neither Wow's own, the
7
+ * actuator's, nor an aggregate's, holding a method per operation.
8
+ *
9
+ * @param warnings - Receives a line for every operation left out and every
10
+ * client renamed
11
+ * @throws GeneratorError when two operations of a client name the same
12
+ * method, or a configured or extension method name is not one
13
+ */
14
+ export declare function analyzeApiClients(document: OpenApiDocument, wow: WowModel, config: GeneratorConfiguration, warnings: string[]): ApiClientModel[];
@@ -0,0 +1,14 @@
1
+ import { GeneratorConfiguration } from '../api/configuration.js';
2
+ import { OpenApiDocument } from '../openapi/document.js';
3
+ import { WowModel } from '../wow/model.js';
4
+ import { ApiClientModel } from './model.js';
5
+ /**
6
+ * The API clients of a document: one per tag that is neither Wow's own, the
7
+ * actuator's, nor an aggregate's, holding a method per operation.
8
+ *
9
+ * @param warnings - Receives a line for every operation left out and every
10
+ * client renamed
11
+ * @throws GeneratorError when two operations of a client name the same
12
+ * method, or a configured or extension method name is not one
13
+ */
14
+ export declare function analyzeApiClients(document: OpenApiDocument, wow: WowModel, config: GeneratorConfiguration, warnings: string[]): ApiClientModel[];