@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
package/dist/index.js ADDED
@@ -0,0 +1,968 @@
1
+ import { VariableDeclarationKind as S, Scope as F } from "ts-morph";
2
+ import { parse as L } from "yaml";
3
+ import { readFile as K } from "fs";
4
+ import { ContentTypeValues as P, combineURLs as A } from "@ahoo-wang/fetcher";
5
+ import { ResourceAttributionPathSpec as R } from "@ahoo-wang/fetcher-wow";
6
+ function h(r) {
7
+ return r.$ref.split("/").pop();
8
+ }
9
+ function E(r, e) {
10
+ const n = h(r);
11
+ return e.schemas?.[n];
12
+ }
13
+ function Q(r, e) {
14
+ const n = h(r);
15
+ return e.requestBodies?.[n];
16
+ }
17
+ function k(r, e) {
18
+ const n = h(r);
19
+ return e.parameters?.[n];
20
+ }
21
+ function y(r, e) {
22
+ return {
23
+ key: h(r),
24
+ schema: E(r, e)
25
+ };
26
+ }
27
+ const x = /[-_\s.]+|(?=[A-Z])/;
28
+ function O(r) {
29
+ if (r === "" || r.length === 0)
30
+ return "";
31
+ let e;
32
+ return Array.isArray(r) ? e = r.flatMap((n) => n.split(x)) : e = r.split(x), e.filter((n) => n.length > 0).map((n) => {
33
+ if (n.length === 0) return "";
34
+ const t = n.charAt(0), o = n.slice(1);
35
+ return (/[a-zA-Z]/.test(t) ? t.toUpperCase() : t) + o.toLowerCase();
36
+ }).join("");
37
+ }
38
+ function M(r) {
39
+ const e = O(r);
40
+ return e.charAt(0).toLowerCase() + e.slice(1);
41
+ }
42
+ function U(r) {
43
+ return r.startsWith("http://") || r.startsWith("https://") ? J(r) : V(r);
44
+ }
45
+ async function J(r) {
46
+ return await (await fetch(r)).text();
47
+ }
48
+ function V(r) {
49
+ return new Promise((e, n) => {
50
+ K(r, "utf-8", (t, o) => {
51
+ t ? n(t) : e(o);
52
+ });
53
+ });
54
+ }
55
+ async function z(r) {
56
+ const e = await U(r);
57
+ switch (H(e)) {
58
+ case "json":
59
+ return JSON.parse(e);
60
+ case "yaml":
61
+ return L(e);
62
+ default:
63
+ throw new Error(`Unsupported file format: ${r}`);
64
+ }
65
+ }
66
+ function H(r) {
67
+ const e = r.trimStart();
68
+ if (e.startsWith("{") || e.startsWith("["))
69
+ return "json";
70
+ if (e.startsWith("-") || e.startsWith("%YAML"))
71
+ return "yaml";
72
+ try {
73
+ return JSON.parse(e), "json";
74
+ } catch {
75
+ if (e.length > 0)
76
+ return "yaml";
77
+ }
78
+ throw new Error("Unable to infer file format");
79
+ }
80
+ function l(r) {
81
+ return !!(r && typeof r == "object" && "$ref" in r);
82
+ }
83
+ function Y(r) {
84
+ return !r || l(r) || !r.content ? void 0 : r.content[P.APPLICATION_JSON]?.schema;
85
+ }
86
+ function Z(r) {
87
+ return [
88
+ { method: "get", operation: r.get },
89
+ { method: "put", operation: r.put },
90
+ { method: "post", operation: r.post },
91
+ { method: "delete", operation: r.delete },
92
+ { method: "options", operation: r.options },
93
+ { method: "head", operation: r.head },
94
+ { method: "patch", operation: r.patch },
95
+ { method: "trace", operation: r.trace }
96
+ ].filter(({ operation: e }) => e !== void 0);
97
+ }
98
+ function j(r) {
99
+ return r.responses[200];
100
+ }
101
+ function b(r) {
102
+ const e = j(r);
103
+ return Y(e);
104
+ }
105
+ function X(r) {
106
+ return Array.isArray(r.enum) && r.enum.length > 0;
107
+ }
108
+ function $(r) {
109
+ if (Array.isArray(r))
110
+ return r.map((e) => $(e)).join(" | ");
111
+ switch (r) {
112
+ case "string":
113
+ return "string";
114
+ case "number":
115
+ case "integer":
116
+ return "number";
117
+ case "boolean":
118
+ return "boolean";
119
+ case "null":
120
+ return "null";
121
+ default:
122
+ return "any";
123
+ }
124
+ }
125
+ const ee = "types.ts", w = "@";
126
+ function q(r) {
127
+ return A(r.path, ee);
128
+ }
129
+ function T(r, e, n) {
130
+ const t = A(e, n), o = r.getSourceFile(t);
131
+ return o || r.createSourceFile(t, "", {
132
+ overwrite: !0
133
+ });
134
+ }
135
+ function N(r, e, n) {
136
+ let t = r.getImportDeclaration(
137
+ (o) => o.getModuleSpecifierValue() === e
138
+ );
139
+ t || (t = r.addImportDeclaration({
140
+ moduleSpecifier: e
141
+ })), n.forEach((o) => {
142
+ t.getNamedImports().some(
143
+ (s) => s.getName() === o
144
+ ) || t.addNamedImport(o);
145
+ });
146
+ }
147
+ function f(r, e, n) {
148
+ let t = q(n);
149
+ n.path.startsWith(w) || (t = A(e, t));
150
+ let o = t;
151
+ t.startsWith(w) || (o = A(w, t)), N(r, o, [n.name]);
152
+ }
153
+ function te(r, e, n, t) {
154
+ r.path !== t.path && f(e, n, t);
155
+ }
156
+ function _(r, e) {
157
+ const n = [r, e].filter(
158
+ (t) => t !== void 0 && t.length > 0
159
+ );
160
+ return n.length > 0 ? n.join(`
161
+ `) : void 0;
162
+ }
163
+ function D(r, e) {
164
+ const n = _(r, e);
165
+ return n ? [n] : [];
166
+ }
167
+ function G(r, e, n) {
168
+ const t = _(e, n);
169
+ t && r.addJsDoc({
170
+ description: t
171
+ });
172
+ }
173
+ function ne(r) {
174
+ const e = r.split(".");
175
+ return e.length != 2 || e[0].length === 0 || e[1].length === 0 ? null : e;
176
+ }
177
+ function re(r) {
178
+ const e = ne(r.name);
179
+ return e ? {
180
+ tag: r,
181
+ contextAlias: e[0],
182
+ aggregateName: e[1]
183
+ } : null;
184
+ }
185
+ function oe(r) {
186
+ const e = r?.map((t) => re(t)).filter((t) => t !== null);
187
+ if (!e)
188
+ return /* @__PURE__ */ new Map();
189
+ const n = /* @__PURE__ */ new Map();
190
+ return e.forEach((t) => {
191
+ n.set(t.tag.name, {
192
+ aggregate: t,
193
+ commands: /* @__PURE__ */ new Map(),
194
+ events: /* @__PURE__ */ new Map()
195
+ });
196
+ }), n;
197
+ }
198
+ function ae(r) {
199
+ if (!r)
200
+ return null;
201
+ const e = r.split(".");
202
+ return e.length != 3 ? null : e[2];
203
+ }
204
+ const se = "#/components/responses/wow.CommandOk", ie = "#/components/parameters/wow.id";
205
+ class ce {
206
+ /**
207
+ * Creates a new AggregateResolver instance.
208
+ * @param openAPI - The OpenAPI specification to resolve aggregates from
209
+ */
210
+ constructor(e) {
211
+ this.openAPI = e, this.aggregates = oe(e.tags), this.build();
212
+ }
213
+ aggregates;
214
+ /**
215
+ * Builds the aggregate definitions by processing all operations in the OpenAPI spec.
216
+ * @private
217
+ */
218
+ build() {
219
+ for (const [e, n] of Object.entries(this.openAPI.paths)) {
220
+ const t = Z(n);
221
+ for (const o of t)
222
+ this.commands(e, o), this.state(o.operation), this.events(o.operation), this.fields(o.operation);
223
+ }
224
+ }
225
+ /**
226
+ * Returns the resolved aggregate definitions.
227
+ * @returns Map of aggregate definitions keyed by context alias
228
+ */
229
+ resolve() {
230
+ const e = /* @__PURE__ */ new Map();
231
+ for (const n of this.aggregates.values()) {
232
+ if (!n.state || !n.fields)
233
+ continue;
234
+ const t = n.aggregate.contextAlias;
235
+ let o = e.get(t);
236
+ o || (o = /* @__PURE__ */ new Set(), e.set(t, o)), o.add(n);
237
+ }
238
+ return e;
239
+ }
240
+ /**
241
+ * Processes command operations and adds them to the appropriate aggregates.
242
+ * @param path - The API path
243
+ * @param methodOperation - The HTTP method and operation details
244
+ */
245
+ commands(e, n) {
246
+ const t = n.operation;
247
+ if (t.operationId === "wow.command.send")
248
+ return;
249
+ const o = ae(t.operationId);
250
+ if (!o)
251
+ return;
252
+ const a = j(t);
253
+ if (!a || !l(a) || a.$ref !== se || !t.requestBody)
254
+ return;
255
+ const s = t.parameters ?? [], c = s.filter((m) => l(m) && m.$ref === ie).at(0), i = s.filter(
256
+ (m) => !l(m) && m.in === "path"
257
+ );
258
+ if (c) {
259
+ const m = k(
260
+ c,
261
+ this.openAPI.components
262
+ );
263
+ i.push(m);
264
+ }
265
+ const u = t.requestBody.content[P.APPLICATION_JSON].schema, d = y(
266
+ u,
267
+ this.openAPI.components
268
+ );
269
+ d.schema.title = d.schema.title || t.summary, d.schema.description = d.schema.description || t.description;
270
+ const B = {
271
+ name: o,
272
+ method: n.method,
273
+ path: e,
274
+ pathParameters: i,
275
+ summary: t.summary,
276
+ description: t.description,
277
+ schema: d,
278
+ operation: t
279
+ };
280
+ t.tags?.forEach((m) => {
281
+ const I = this.aggregates.get(m);
282
+ I && I.commands.set(o, B);
283
+ });
284
+ }
285
+ /**
286
+ * Processes state snapshot operations and associates them with aggregates.
287
+ * @param operation - The OpenAPI operation
288
+ */
289
+ state(e) {
290
+ if (!e.operationId?.endsWith(".snapshot_state.single"))
291
+ return;
292
+ const n = b(e);
293
+ if (!l(n))
294
+ return;
295
+ const t = y(
296
+ n,
297
+ this.openAPI.components
298
+ );
299
+ e.tags?.forEach((o) => {
300
+ const a = this.aggregates.get(o);
301
+ a && (a.state = t);
302
+ });
303
+ }
304
+ /**
305
+ * Processes event stream operations and extracts domain events for aggregates.
306
+ * @param operation - The OpenAPI operation
307
+ */
308
+ events(e) {
309
+ if (!this.openAPI.components || !e.operationId?.endsWith(".event.list_query"))
310
+ return;
311
+ const n = b(e);
312
+ if (l(n))
313
+ return;
314
+ const t = n?.items;
315
+ if (!l(t))
316
+ return;
317
+ const a = E(
318
+ t,
319
+ this.openAPI.components
320
+ ).properties.body.items.anyOf.map((s) => {
321
+ const c = s.title, i = s.properties.name.const, p = s.properties.body, u = y(
322
+ p,
323
+ this.openAPI.components
324
+ );
325
+ return u.schema.title = u.schema.title || s.title, {
326
+ title: c,
327
+ name: i,
328
+ schema: u
329
+ };
330
+ });
331
+ e.tags?.forEach((s) => {
332
+ const c = this.aggregates.get(s);
333
+ c && a.forEach((i) => {
334
+ c.events.set(i.name, i);
335
+ });
336
+ });
337
+ }
338
+ /**
339
+ * Processes field query operations and associates field schemas with aggregates.
340
+ * @param operation - The OpenAPI operation
341
+ */
342
+ fields(e) {
343
+ if (!this.openAPI.components || !e.operationId?.endsWith(".snapshot.count"))
344
+ return;
345
+ const t = Q(
346
+ e.requestBody,
347
+ this.openAPI.components
348
+ ).content[P.APPLICATION_JSON].schema, a = E(
349
+ t,
350
+ this.openAPI.components
351
+ ).properties?.field, s = y(a, this.openAPI.components);
352
+ e.tags?.forEach((c) => {
353
+ const i = this.aggregates.get(c);
354
+ i && (i.fields = s);
355
+ });
356
+ }
357
+ }
358
+ const v = "@ahoo-wang/fetcher-wow", pe = {
359
+ "wow.command.CommandResult": "CommandResult",
360
+ "wow.MessageHeaderSqlType": "MessageHeaderSqlType",
361
+ "wow.api.BindingError": "BindingError",
362
+ "wow.api.DefaultErrorInfo": "ErrorInfo",
363
+ "wow.api.RecoverableType": "RecoverableType",
364
+ "wow.api.command.DefaultDeleteAggregate": "DeleteAggregate",
365
+ "wow.api.command.DefaultRecoverAggregate": "RecoverAggregate",
366
+ "wow.api.messaging.FunctionInfoData": "FunctionInfo",
367
+ "wow.api.messaging.FunctionKind": "FunctionKind",
368
+ "wow.api.modeling.AggregateId": "AggregateId",
369
+ "wow.api.query.Condition": "Condition",
370
+ "wow.api.query.ConditionOptions": "ConditionOptions",
371
+ "wow.api.query.ListQuery": "ListQuery",
372
+ "wow.api.query.Operator": "Operator",
373
+ "wow.api.query.PagedQuery": "PagedQuery",
374
+ "wow.api.query.Pagination": "Pagination",
375
+ "wow.api.query.Projection": "Projection",
376
+ "wow.api.query.Sort": "FieldSort",
377
+ "wow.api.query.Sort.Direction": "SortDirection",
378
+ "wow.command.CommandStage": "CommandStage",
379
+ "wow.command.SimpleWaitSignal": "WaitSignal",
380
+ "wow.configuration.Aggregate": "Aggregate",
381
+ "wow.configuration.BoundedContext": "BoundedContext",
382
+ "wow.configuration.WowMetadata": "WowMetadata",
383
+ "wow.modeling.DomainEvent": "DomainEvent",
384
+ "wow.openapi.BatchResult": "BatchResult",
385
+ "wow.messaging.CompensationTarget": "CompensationTarget"
386
+ };
387
+ function g(r) {
388
+ if (!r)
389
+ return { name: "", path: "/" };
390
+ const e = pe[r];
391
+ if (e)
392
+ return { name: e, path: v };
393
+ const n = r.split(".");
394
+ let t = -1;
395
+ for (let i = 0; i < n.length; i++)
396
+ if (n[i] && /^[A-Z]/.test(n[i])) {
397
+ t = i;
398
+ break;
399
+ }
400
+ if (t === -1)
401
+ return { name: r, path: "/" };
402
+ const o = n.slice(0, t), a = o.length > 0 ? `/${o.join("/")}` : "/", s = n.slice(t);
403
+ return { name: O(s), path: a };
404
+ }
405
+ class C {
406
+ project;
407
+ openAPI;
408
+ outputDir;
409
+ contextAggregates;
410
+ logger;
411
+ /**
412
+ * Creates a new ClientGenerator instance.
413
+ * @param context - The generation context containing OpenAPI spec and project details
414
+ */
415
+ constructor(e) {
416
+ this.project = e.project, this.openAPI = e.openAPI, this.outputDir = e.outputDir, this.contextAggregates = e.contextAggregates, this.logger = e.logger;
417
+ }
418
+ }
419
+ class me extends C {
420
+ constructor(e) {
421
+ super(e);
422
+ }
423
+ getOrCreateSourceFile(e) {
424
+ const n = q(e);
425
+ return T(this.project, this.outputDir, n);
426
+ }
427
+ /**
428
+ * Generates models for all schemas in the OpenAPI specification.
429
+ * Skips schemas with keys starting with 'wow.'.
430
+ *
431
+ * @remarks
432
+ * This method iterates through all schemas in the OpenAPI specification
433
+ * and generates corresponding TypeScript models for each one.
434
+ */
435
+ generate() {
436
+ const e = this.openAPI.components?.schemas;
437
+ if (!e) {
438
+ this.logger?.info("No schemas found in OpenAPI specification");
439
+ return;
440
+ }
441
+ this.logger?.progress(
442
+ `Generating models for ${Object.keys(e).length} schemas`
443
+ ), Object.entries(e).forEach(([n, t]) => {
444
+ n.startsWith("wow.") || (this.logger?.progress(`Processing schema: ${n}`), this.generateKeyedSchema(n, t));
445
+ }), this.logger?.success("Model generation completed");
446
+ }
447
+ /**
448
+ * Generates a model for a specific schema key.
449
+ * Processes enums, objects, unions, and type aliases in order.
450
+ *
451
+ * @param schemaKey - The key of the schema to generate
452
+ * @param schema - The schema definition
453
+ *
454
+ * @remarks
455
+ * The generation process follows this order:
456
+ * 1. Enum processing
457
+ * 2. Object processing
458
+ * 3. Union processing
459
+ * 4. Type alias processing
460
+ */
461
+ generateKeyedSchema(e, n) {
462
+ const t = g(e), o = this.getOrCreateSourceFile(t), a = this.process(t, o, n);
463
+ return G(a, n.title, n.description), o;
464
+ }
465
+ process(e, n, t) {
466
+ let o = this.processEnum(
467
+ e,
468
+ n,
469
+ t
470
+ );
471
+ return o || (o = this.processObject(e, n, t), o) || (o = this.processUnion(e, n, t), o) ? o : this.processTypeAlias(e, n, t);
472
+ }
473
+ /**
474
+ * Processes enum schemas and generates TypeScript enums.
475
+ *
476
+ * @param modelInfo - The model information
477
+ * @param sourceFile - The source file to add the enum to
478
+ * @param schema - The enum schema
479
+ * @returns true if the schema was processed as an enum, false otherwise
480
+ *
481
+ * @remarks
482
+ * This method filters out non-string enum values and generates
483
+ * a TypeScript enum with string literal initializers.
484
+ */
485
+ processEnum(e, n, t) {
486
+ if (X(t))
487
+ return n.addEnum({
488
+ name: e.name,
489
+ isExported: !0,
490
+ members: t.enum.filter((o) => typeof o == "string" && o.length > 0).map((o) => ({
491
+ name: o,
492
+ initializer: `'${o}'`
493
+ }))
494
+ });
495
+ }
496
+ /**
497
+ * Processes object schemas and generates TypeScript interfaces.
498
+ *
499
+ * @param modelInfo - The model information
500
+ * @param sourceFile - The source file to add the interface to
501
+ * @param schema - The object schema
502
+ * @returns true if the schema was processed as an object, false otherwise
503
+ *
504
+ * @remarks
505
+ * This method handles optional properties by checking the required array
506
+ * and adds undefined union types for optional properties.
507
+ */
508
+ processObject(e, n, t) {
509
+ if (t.type !== "object" || !t.properties)
510
+ return;
511
+ const o = {}, a = t.required || [];
512
+ for (const [s, c] of Object.entries(t.properties)) {
513
+ const i = this.resolveType(e, n, c), p = !a.includes(s);
514
+ o[s] = p ? `${i} | undefined` : i;
515
+ }
516
+ return n.addInterface({
517
+ name: e.name,
518
+ isExported: !0,
519
+ properties: Object.entries(o).map(([s, c]) => ({
520
+ name: s,
521
+ type: c
522
+ }))
523
+ });
524
+ }
525
+ /**
526
+ * Processes union schemas (allOf, anyOf, oneOf) and generates TypeScript type aliases.
527
+ *
528
+ * @param modelInfo - The model information
529
+ * @param sourceFile - The source file to add the type alias to
530
+ * @param schema - The union schema
531
+ * @returns true if the schema was processed as a union, false otherwise
532
+ *
533
+ * @remarks
534
+ * This method handles three types of unions:
535
+ * - allOf: Generates intersection types (&)
536
+ * - anyOf: Generates union types (|)
537
+ * - oneOf: Generates union types (|)
538
+ */
539
+ processUnion(e, n, t) {
540
+ let o = null;
541
+ 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)
542
+ return n.addTypeAlias({
543
+ name: e.name,
544
+ type: o,
545
+ isExported: !0,
546
+ docs: D(t.title, t.description)
547
+ });
548
+ }
549
+ /**
550
+ * Processes type alias schemas and generates TypeScript type aliases.
551
+ *
552
+ * @param modelInfo - The model information
553
+ * @param sourceFile - The source file to add the type alias to
554
+ * @param schema - The schema to process
555
+ *
556
+ * @remarks
557
+ * This method is used as a fallback for schemas that don't match
558
+ * enum, object, or union patterns. It resolves the type and creates
559
+ * a simple type alias.
560
+ */
561
+ processTypeAlias(e, n, t) {
562
+ const o = this.resolveType(e, n, t);
563
+ return n.addTypeAlias({
564
+ name: e.name,
565
+ type: o,
566
+ isExported: !0,
567
+ docs: D(t.title, t.description)
568
+ });
569
+ }
570
+ /**
571
+ * Resolves the TypeScript type for a given schema or reference.
572
+ * Handles arrays, objects, primitives, and references.
573
+ *
574
+ * @param modelInfo - The model information
575
+ * @param sourceFile - The source file for import management
576
+ * @param schema - The schema or reference to resolve
577
+ * @returns The resolved TypeScript type as a string
578
+ *
579
+ * @remarks
580
+ * This method handles various schema types:
581
+ * - References: Resolves to imported types
582
+ * - Arrays: Resolves item types and adds array notation
583
+ * - Objects: Resolves to Record<string, any> for generic objects
584
+ * - Primitives: Maps to TypeScript primitives
585
+ * - Nullable: Adds null union type
586
+ */
587
+ resolveType(e, n, t) {
588
+ if (l(t))
589
+ return this.resolveReference(e, n, t);
590
+ if (t.type === "array")
591
+ return `${t.items ? this.resolveType(e, n, t.items) : "any"}[]`;
592
+ if (t.type === "object" && !t.properties)
593
+ return "Record<string, any>";
594
+ let o;
595
+ return t.type ? o = $(t.type) : o = "any", t.nullable && (o = `${o} | null`), o;
596
+ }
597
+ /**
598
+ * Resolves a reference to another schema.
599
+ * Handles mapped types and imports.
600
+ *
601
+ * @param modelInfo - The current model information
602
+ * @param sourceFile - The source file for import management
603
+ * @param ref - The reference to resolve
604
+ * @returns The resolved type name
605
+ *
606
+ * @remarks
607
+ * This method checks for mapped types first (WOW_TYPE_MAPPING).
608
+ * If not found, it adds an import for the referenced model.
609
+ */
610
+ resolveReference(e, n, t) {
611
+ const o = h(t), a = g(o);
612
+ return te(e, n, this.outputDir, a), a.name;
613
+ }
614
+ }
615
+ function ue(r) {
616
+ let e = 0, n = 0;
617
+ return r.commands.forEach((t) => {
618
+ t.path.startsWith(R.TENANT) && (e += 1), t.path.startsWith(R.OWNER) && (n += 1);
619
+ }), e === 0 && n === 0 ? "ResourceAttributionPathSpec.NONE" : e > n ? "ResourceAttributionPathSpec.TENANT" : "ResourceAttributionPathSpec.OWNER";
620
+ }
621
+ function W(r, e, n, t) {
622
+ const o = `${n.contextAlias}/${n.aggregateName}/${t}.ts`;
623
+ return T(r, e, o);
624
+ }
625
+ function le(r, e) {
626
+ return `${O(r.aggregateName)}${e}`;
627
+ }
628
+ class ge extends C {
629
+ /**
630
+ * Creates a new QueryClientGenerator instance.
631
+ * @param context - The generation context containing OpenAPI spec and project details
632
+ */
633
+ constructor(e) {
634
+ super(e);
635
+ }
636
+ /**
637
+ * Generates query client classes for all aggregates.
638
+ */
639
+ generate() {
640
+ const e = Array.from(this.contextAggregates.values()).flat().length;
641
+ this.logger?.progress(
642
+ `Generating query clients for ${e} aggregates`
643
+ );
644
+ for (const [, n] of this.contextAggregates)
645
+ n.forEach((t) => {
646
+ this.logger?.progress(
647
+ `Processing query client for aggregate: ${t.aggregate.aggregateName}`
648
+ ), this.processQueryClient(t);
649
+ });
650
+ this.logger?.success("Query client generation completed");
651
+ }
652
+ /**
653
+ * Creates or retrieves a source file for client generation.
654
+ * @param aggregate - The aggregate metadata
655
+ * @param fileName - The name of the client file
656
+ * @returns The source file for the client
657
+ */
658
+ createClientFilePath(e, n) {
659
+ return W(
660
+ this.project,
661
+ this.outputDir,
662
+ e,
663
+ n
664
+ );
665
+ }
666
+ /**
667
+ * Processes and generates query client classes for an aggregate.
668
+ * @param aggregate - The aggregate definition
669
+ */
670
+ processQueryClient(e) {
671
+ const n = this.createClientFilePath(
672
+ e.aggregate,
673
+ "queryClient"
674
+ );
675
+ n.addImportDeclaration({
676
+ moduleSpecifier: v,
677
+ namedImports: [
678
+ "QueryClientFactory",
679
+ "QueryClientOptions",
680
+ "ResourceAttributionPathSpec"
681
+ ]
682
+ });
683
+ const t = "DEFAULT_QUERY_CLIENT_OPTIONS";
684
+ n.addVariableStatement({
685
+ declarationKind: S.Const,
686
+ declarations: [
687
+ {
688
+ name: t,
689
+ type: "QueryClientOptions",
690
+ initializer: `{
691
+ contextAlias: '${e.aggregate.contextAlias}',
692
+ aggregateName: '${e.aggregate.aggregateName}',
693
+ resourceAttribution: ${ue(e)},
694
+ }`
695
+ }
696
+ ],
697
+ isExported: !1
698
+ });
699
+ const o = [];
700
+ for (const p of e.events.values()) {
701
+ const u = g(p.schema.key);
702
+ f(n, this.outputDir, u), o.push(u);
703
+ }
704
+ const a = "DOMAIN_EVENT_TYPES";
705
+ n.addTypeAlias({
706
+ name: a,
707
+ type: o.map((p) => p.name).join(" | ")
708
+ });
709
+ const s = `${M(e.aggregate.aggregateName)}QueryClientFactory`, c = g(e.state.key), i = g(e.fields.key);
710
+ f(n, this.outputDir, c), f(n, this.outputDir, i), n.addVariableStatement({
711
+ declarationKind: S.Const,
712
+ declarations: [
713
+ {
714
+ name: s,
715
+ initializer: `new QueryClientFactory<${c.name}, ${i.name} | string, ${a}>(${t})`
716
+ }
717
+ ],
718
+ isExported: !0
719
+ });
720
+ }
721
+ }
722
+ class de extends C {
723
+ commandEndpointPathsName = "COMMAND_ENDPOINT_PATHS";
724
+ defaultCommandClientOptionsName = "DEFAULT_COMMAND_CLIENT_OPTIONS";
725
+ /**
726
+ * Creates a new CommandClientGenerator instance.
727
+ * @param context - The generation context containing OpenAPI spec and project details
728
+ */
729
+ constructor(e) {
730
+ super(e);
731
+ }
732
+ /**
733
+ * Generates command client classes for all aggregates.
734
+ */
735
+ generate() {
736
+ const e = Array.from(this.contextAggregates.values()).flat().length;
737
+ this.logger?.progress(
738
+ `Generating command clients for ${e} aggregates`
739
+ );
740
+ for (const [, n] of this.contextAggregates)
741
+ n.forEach((t) => {
742
+ this.logger?.progress(
743
+ `Processing command client for aggregate: ${t.aggregate.aggregateName}`
744
+ ), this.processAggregate(t);
745
+ });
746
+ this.logger?.success("Command client generation completed");
747
+ }
748
+ /**
749
+ * Processes and generates command client for an aggregate.
750
+ * @param aggregate - The aggregate definition
751
+ */
752
+ processAggregate(e) {
753
+ const n = W(
754
+ this.project,
755
+ this.outputDir,
756
+ e.aggregate,
757
+ "commandClient"
758
+ );
759
+ this.processCommandEndpointPaths(n, e), n.addVariableStatement({
760
+ declarationKind: S.Const,
761
+ declarations: [
762
+ {
763
+ name: this.defaultCommandClientOptionsName,
764
+ type: "ApiMetadata",
765
+ initializer: `{
766
+ basePath: '${e.aggregate.contextAlias}'
767
+ }`
768
+ }
769
+ ],
770
+ isExported: !1
771
+ }), n.addImportDeclaration({
772
+ moduleSpecifier: v,
773
+ namedImports: [
774
+ "CommandRequest",
775
+ "CommandResult",
776
+ "CommandResultEventStream",
777
+ "DeleteAggregate",
778
+ "RecoverAggregate"
779
+ ],
780
+ isTypeOnly: !0
781
+ }), n.addImportDeclaration({
782
+ moduleSpecifier: "@ahoo-wang/fetcher-eventstream",
783
+ namedImports: ["JsonEventStreamResultExtractor"]
784
+ }), N(n, "@ahoo-wang/fetcher", ["ContentTypeValues"]), N(n, "@ahoo-wang/fetcher-decorator", [
785
+ "type ApiMetadata",
786
+ "type ApiMetadataCapable",
787
+ "api",
788
+ "post",
789
+ "put",
790
+ "del",
791
+ "request",
792
+ "attribute",
793
+ "path",
794
+ "autoGeneratedError"
795
+ ]), this.processCommandClient(n, e), this.processCommandClient(n, e, !0);
796
+ }
797
+ processCommandEndpointPaths(e, n) {
798
+ const t = e.addEnum({
799
+ name: this.commandEndpointPathsName
800
+ });
801
+ n.commands.forEach((o) => {
802
+ t.addMember({
803
+ name: o.name.toUpperCase(),
804
+ initializer: `'${o.path}'`
805
+ });
806
+ });
807
+ }
808
+ getEndpointPath(e) {
809
+ return `${this.commandEndpointPathsName}.${e.name.toUpperCase()}`;
810
+ }
811
+ processCommandClient(e, n, t = !1) {
812
+ let o = "CommandClient", a = {
813
+ name: "api",
814
+ arguments: []
815
+ }, s = "Promise<CommandResult>";
816
+ t && (o = "Stream" + o, a = {
817
+ name: "api",
818
+ arguments: [
819
+ "''",
820
+ `{
821
+ headers: { Accept: ContentTypeValues.TEXT_EVENT_STREAM },
822
+ resultExtractor: JsonEventStreamResultExtractor,
823
+ }`
824
+ ]
825
+ }, s = "Promise<CommandResultEventStream>");
826
+ const c = le(
827
+ n.aggregate,
828
+ o
829
+ ), i = e.addClass({
830
+ name: c,
831
+ isExported: !0,
832
+ decorators: [a],
833
+ implements: ["ApiMetadataCapable"]
834
+ });
835
+ i.addConstructor({
836
+ parameters: [
837
+ {
838
+ name: "apiMetadata",
839
+ type: "ApiMetadata",
840
+ scope: F.Public,
841
+ isReadonly: !0,
842
+ initializer: `${this.defaultCommandClientOptionsName}`
843
+ }
844
+ ]
845
+ }), n.commands.forEach((p) => {
846
+ this.processCommandMethod(e, i, p, s);
847
+ });
848
+ }
849
+ methodToDecorator(e) {
850
+ return e === "delete" ? "del" : e;
851
+ }
852
+ /**
853
+ * Processes and generates a command method for the command client.
854
+ * @param sourceFile - The source file containing the client
855
+ * @param client - The client class declaration
856
+ * @param definition - The command definition
857
+ */
858
+ processCommandMethod(e, n, t, o) {
859
+ const a = g(t.schema.key);
860
+ f(e, this.outputDir, a);
861
+ const s = t.pathParameters.map((i) => ({
862
+ name: i.name,
863
+ type: "string",
864
+ decorators: [
865
+ {
866
+ name: "path",
867
+ arguments: [`'${i.name}'`]
868
+ }
869
+ ]
870
+ }));
871
+ s.push({
872
+ name: "commandRequest",
873
+ type: `CommandRequest<${a.name}>`,
874
+ decorators: [
875
+ {
876
+ name: "request",
877
+ arguments: []
878
+ }
879
+ ]
880
+ }), s.push({
881
+ name: "attributes",
882
+ type: "Record<string, any>",
883
+ decorators: [
884
+ {
885
+ name: "attribute",
886
+ arguments: []
887
+ }
888
+ ]
889
+ });
890
+ const c = n.addMethod({
891
+ name: M(t.name),
892
+ decorators: [
893
+ {
894
+ name: this.methodToDecorator(t.method),
895
+ arguments: [`${this.getEndpointPath(t)}`]
896
+ }
897
+ ],
898
+ parameters: s,
899
+ returnType: o,
900
+ statements: [
901
+ `throw autoGeneratedError(${s.map((i) => i.name).join(",")});`
902
+ ]
903
+ });
904
+ G(c, t.summary, t.description);
905
+ }
906
+ }
907
+ class fe extends C {
908
+ queryClientGenerator;
909
+ commandClientGenerator;
910
+ /**
911
+ * Creates a new ClientGenerator instance.
912
+ * @param context - The generation context containing OpenAPI spec and project details
913
+ */
914
+ constructor(e) {
915
+ super(e), this.queryClientGenerator = new ge(e), this.commandClientGenerator = new de(e);
916
+ }
917
+ /**
918
+ * Generates client classes for all aggregates.
919
+ */
920
+ generate() {
921
+ this.logger?.progress(
922
+ `Generating clients for ${this.contextAggregates.size} bounded contexts`
923
+ );
924
+ for (const [e] of this.contextAggregates)
925
+ this.logger?.progress(`Processing bounded context: ${e}`), this.processBoundedContext(e);
926
+ this.queryClientGenerator.generate(), this.commandClientGenerator.generate(), this.logger?.success("Client generation completed");
927
+ }
928
+ /**
929
+ * Processes a bounded context by creating a file with the context alias constant.
930
+ * @param contextAlias - The alias of the bounded context to process
931
+ */
932
+ processBoundedContext(e) {
933
+ const n = `${e}/boundedContext.ts`;
934
+ T(this.project, this.outputDir, n).addStatements(
935
+ `export const BOUNDED_CONTEXT_ALIAS = '${e}';`
936
+ );
937
+ }
938
+ }
939
+ class Se {
940
+ /**
941
+ * Creates a new CodeGenerator instance.
942
+ * @param options - Configuration options for code generation
943
+ */
944
+ constructor(e) {
945
+ this.options = e, this.project = e.project;
946
+ }
947
+ project;
948
+ /**
949
+ * Generates TypeScript code from the OpenAPI specification.
950
+ * Parses the OpenAPI spec, resolves aggregates, generates models and clients,
951
+ * and formats the output files.
952
+ */
953
+ async generate() {
954
+ const e = await z(this.options.inputPath), t = new ce(e).resolve(), o = {
955
+ openAPI: e,
956
+ project: this.project,
957
+ outputDir: this.options.outputDir,
958
+ contextAggregates: t,
959
+ logger: this.options.logger
960
+ };
961
+ new me(o).generate(), new fe(o).generate(), this.project.getSourceFiles().forEach((c) => {
962
+ c.formatText(), c.organizeImports(), c.fixMissingImports();
963
+ }), await this.project.save();
964
+ }
965
+ }
966
+ export {
967
+ Se as CodeGenerator
968
+ };