@hexaijs/plugin-contracts-generator 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +38 -27
  2. package/dist/cli-DajurpEQ.d.ts +277 -0
  3. package/dist/cli.d.ts +1 -35
  4. package/dist/cli.js +2474 -233
  5. package/dist/cli.js.map +1 -1
  6. package/dist/index.d.ts +461 -40
  7. package/dist/index.js +2711 -91
  8. package/dist/index.js.map +1 -1
  9. package/dist/runtime/index.d.ts +23 -2
  10. package/dist/runtime/index.js +38 -5
  11. package/dist/runtime/index.js.map +1 -1
  12. package/package.json +11 -19
  13. package/dist/ast-utils.d.ts +0 -6
  14. package/dist/ast-utils.d.ts.map +0 -1
  15. package/dist/ast-utils.js +0 -111
  16. package/dist/ast-utils.js.map +0 -1
  17. package/dist/class-analyzer.d.ts +0 -16
  18. package/dist/class-analyzer.d.ts.map +0 -1
  19. package/dist/class-analyzer.js +0 -155
  20. package/dist/class-analyzer.js.map +0 -1
  21. package/dist/cli.d.ts.map +0 -1
  22. package/dist/config-loader.d.ts +0 -34
  23. package/dist/config-loader.d.ts.map +0 -1
  24. package/dist/config-loader.js +0 -188
  25. package/dist/config-loader.js.map +0 -1
  26. package/dist/decorators/index.d.ts +0 -143
  27. package/dist/decorators/index.d.ts.map +0 -1
  28. package/dist/decorators/index.js +0 -123
  29. package/dist/decorators/index.js.map +0 -1
  30. package/dist/domain/index.d.ts +0 -2
  31. package/dist/domain/index.d.ts.map +0 -1
  32. package/dist/domain/index.js +0 -18
  33. package/dist/domain/index.js.map +0 -1
  34. package/dist/domain/types.d.ts +0 -182
  35. package/dist/domain/types.d.ts.map +0 -1
  36. package/dist/domain/types.js +0 -65
  37. package/dist/domain/types.js.map +0 -1
  38. package/dist/errors.d.ts +0 -79
  39. package/dist/errors.d.ts.map +0 -1
  40. package/dist/errors.js +0 -138
  41. package/dist/errors.js.map +0 -1
  42. package/dist/file-copier.d.ts +0 -84
  43. package/dist/file-copier.d.ts.map +0 -1
  44. package/dist/file-copier.js +0 -758
  45. package/dist/file-copier.js.map +0 -1
  46. package/dist/file-graph-resolver.d.ts +0 -47
  47. package/dist/file-graph-resolver.d.ts.map +0 -1
  48. package/dist/file-graph-resolver.js +0 -230
  49. package/dist/file-graph-resolver.js.map +0 -1
  50. package/dist/file-system.d.ts +0 -26
  51. package/dist/file-system.d.ts.map +0 -1
  52. package/dist/file-system.js +0 -34
  53. package/dist/file-system.js.map +0 -1
  54. package/dist/hexai-plugin.d.ts +0 -16
  55. package/dist/hexai-plugin.d.ts.map +0 -1
  56. package/dist/hexai-plugin.js +0 -59
  57. package/dist/hexai-plugin.js.map +0 -1
  58. package/dist/import-analyzer.d.ts +0 -6
  59. package/dist/import-analyzer.d.ts.map +0 -1
  60. package/dist/import-analyzer.js +0 -77
  61. package/dist/import-analyzer.js.map +0 -1
  62. package/dist/index.d.ts.map +0 -1
  63. package/dist/logger.d.ts +0 -21
  64. package/dist/logger.d.ts.map +0 -1
  65. package/dist/logger.js +0 -47
  66. package/dist/logger.js.map +0 -1
  67. package/dist/parser.d.ts +0 -32
  68. package/dist/parser.d.ts.map +0 -1
  69. package/dist/parser.js +0 -209
  70. package/dist/parser.js.map +0 -1
  71. package/dist/pipeline.d.ts +0 -56
  72. package/dist/pipeline.d.ts.map +0 -1
  73. package/dist/pipeline.js +0 -152
  74. package/dist/pipeline.js.map +0 -1
  75. package/dist/reexport-generator.d.ts +0 -81
  76. package/dist/reexport-generator.d.ts.map +0 -1
  77. package/dist/reexport-generator.js +0 -208
  78. package/dist/reexport-generator.js.map +0 -1
  79. package/dist/registry-generator.d.ts +0 -27
  80. package/dist/registry-generator.d.ts.map +0 -1
  81. package/dist/registry-generator.js +0 -108
  82. package/dist/registry-generator.js.map +0 -1
  83. package/dist/runtime/index.d.ts.map +0 -1
  84. package/dist/runtime/message-registry.d.ts +0 -23
  85. package/dist/runtime/message-registry.d.ts.map +0 -1
  86. package/dist/runtime/message-registry.js +0 -39
  87. package/dist/runtime/message-registry.js.map +0 -1
  88. package/dist/scanner.d.ts +0 -21
  89. package/dist/scanner.d.ts.map +0 -1
  90. package/dist/scanner.js +0 -53
  91. package/dist/scanner.js.map +0 -1
  92. package/dist/test-utils.d.ts +0 -23
  93. package/dist/test-utils.d.ts.map +0 -1
  94. package/dist/test-utils.js +0 -198
  95. package/dist/test-utils.js.map +0 -1
  96. package/dist/tsconfig-loader.d.ts +0 -8
  97. package/dist/tsconfig-loader.d.ts.map +0 -1
  98. package/dist/tsconfig-loader.js +0 -64
  99. package/dist/tsconfig-loader.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,97 +1,2717 @@
1
- "use strict";
2
- /**
3
- * Contracts Generator
4
- *
5
- * Extract Domain Events and Commands from TypeScript source code using decorators.
6
- *
7
- * @example
8
- * ```typescript
9
- * import { extract } from '@hexaijs/plugin-contracts-generator';
10
- *
11
- * const result = await extract({
12
- * sourceDir: 'packages',
13
- * outputDir: 'packages/contracts',
14
- * });
15
- *
16
- * console.log(`Extracted ${result.events.length} events and ${result.commands.length} commands`);
17
- * ```
18
- */
19
- Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.cliPlugin = exports.ContractsPipeline = exports.noopLogger = exports.ConsoleLogger = exports.nodeFileSystem = exports.ReexportGenerator = exports.RegistryGenerator = exports.ModuleResolutionError = exports.ResolutionError = exports.JsonParseError = exports.ParseError = exports.FileWriteError = exports.FileReadError = exports.FileNotFoundError = exports.FileSystemError = exports.TsconfigLoadError = exports.ConfigLoadError = exports.ConfigurationError = exports.MessageParserError = exports.ConfigLoader = exports.TsconfigLoader = exports.FileCopier = exports.FileGraphResolver = exports.Parser = exports.Scanner = exports.PublicQuery = exports.PublicCommand = exports.PublicEvent = exports.isCommand = exports.isDomainEvent = exports.isFunctionType = exports.isTupleType = exports.isLiteralType = exports.isReferenceType = exports.isIntersectionType = exports.isUnionType = exports.isObjectType = exports.isArrayType = exports.isPrimitiveType = void 0;
21
- exports.processContext = processContext;
22
- var types_1 = require("./domain/types");
23
- Object.defineProperty(exports, "isPrimitiveType", { enumerable: true, get: function () { return types_1.isPrimitiveType; } });
24
- Object.defineProperty(exports, "isArrayType", { enumerable: true, get: function () { return types_1.isArrayType; } });
25
- Object.defineProperty(exports, "isObjectType", { enumerable: true, get: function () { return types_1.isObjectType; } });
26
- Object.defineProperty(exports, "isUnionType", { enumerable: true, get: function () { return types_1.isUnionType; } });
27
- Object.defineProperty(exports, "isIntersectionType", { enumerable: true, get: function () { return types_1.isIntersectionType; } });
28
- Object.defineProperty(exports, "isReferenceType", { enumerable: true, get: function () { return types_1.isReferenceType; } });
29
- Object.defineProperty(exports, "isLiteralType", { enumerable: true, get: function () { return types_1.isLiteralType; } });
30
- Object.defineProperty(exports, "isTupleType", { enumerable: true, get: function () { return types_1.isTupleType; } });
31
- Object.defineProperty(exports, "isFunctionType", { enumerable: true, get: function () { return types_1.isFunctionType; } });
32
- Object.defineProperty(exports, "isDomainEvent", { enumerable: true, get: function () { return types_1.isDomainEvent; } });
33
- Object.defineProperty(exports, "isCommand", { enumerable: true, get: function () { return types_1.isCommand; } });
34
- var decorators_1 = require("./decorators");
35
- Object.defineProperty(exports, "PublicEvent", { enumerable: true, get: function () { return decorators_1.PublicEvent; } });
36
- Object.defineProperty(exports, "PublicCommand", { enumerable: true, get: function () { return decorators_1.PublicCommand; } });
37
- Object.defineProperty(exports, "PublicQuery", { enumerable: true, get: function () { return decorators_1.PublicQuery; } });
38
- var scanner_1 = require("./scanner");
39
- Object.defineProperty(exports, "Scanner", { enumerable: true, get: function () { return scanner_1.Scanner; } });
40
- var parser_1 = require("./parser");
41
- Object.defineProperty(exports, "Parser", { enumerable: true, get: function () { return parser_1.Parser; } });
42
- var file_graph_resolver_1 = require("./file-graph-resolver");
43
- Object.defineProperty(exports, "FileGraphResolver", { enumerable: true, get: function () { return file_graph_resolver_1.FileGraphResolver; } });
44
- var file_copier_1 = require("./file-copier");
45
- Object.defineProperty(exports, "FileCopier", { enumerable: true, get: function () { return file_copier_1.FileCopier; } });
46
- var tsconfig_loader_1 = require("./tsconfig-loader");
47
- Object.defineProperty(exports, "TsconfigLoader", { enumerable: true, get: function () { return tsconfig_loader_1.TsconfigLoader; } });
48
- var config_loader_1 = require("./config-loader");
49
- Object.defineProperty(exports, "ConfigLoader", { enumerable: true, get: function () { return config_loader_1.ConfigLoader; } });
50
- var errors_1 = require("./errors");
51
- Object.defineProperty(exports, "MessageParserError", { enumerable: true, get: function () { return errors_1.MessageParserError; } });
52
- Object.defineProperty(exports, "ConfigurationError", { enumerable: true, get: function () { return errors_1.ConfigurationError; } });
53
- Object.defineProperty(exports, "ConfigLoadError", { enumerable: true, get: function () { return errors_1.ConfigLoadError; } });
54
- Object.defineProperty(exports, "TsconfigLoadError", { enumerable: true, get: function () { return errors_1.TsconfigLoadError; } });
55
- Object.defineProperty(exports, "FileSystemError", { enumerable: true, get: function () { return errors_1.FileSystemError; } });
56
- Object.defineProperty(exports, "FileNotFoundError", { enumerable: true, get: function () { return errors_1.FileNotFoundError; } });
57
- Object.defineProperty(exports, "FileReadError", { enumerable: true, get: function () { return errors_1.FileReadError; } });
58
- Object.defineProperty(exports, "FileWriteError", { enumerable: true, get: function () { return errors_1.FileWriteError; } });
59
- Object.defineProperty(exports, "ParseError", { enumerable: true, get: function () { return errors_1.ParseError; } });
60
- Object.defineProperty(exports, "JsonParseError", { enumerable: true, get: function () { return errors_1.JsonParseError; } });
61
- Object.defineProperty(exports, "ResolutionError", { enumerable: true, get: function () { return errors_1.ResolutionError; } });
62
- Object.defineProperty(exports, "ModuleResolutionError", { enumerable: true, get: function () { return errors_1.ModuleResolutionError; } });
63
- var registry_generator_1 = require("./registry-generator");
64
- Object.defineProperty(exports, "RegistryGenerator", { enumerable: true, get: function () { return registry_generator_1.RegistryGenerator; } });
65
- var reexport_generator_1 = require("./reexport-generator");
66
- Object.defineProperty(exports, "ReexportGenerator", { enumerable: true, get: function () { return reexport_generator_1.ReexportGenerator; } });
67
- const file_system_1 = require("./file-system");
68
- const logger_1 = require("./logger");
69
- const pipeline_1 = require("./pipeline");
70
- var file_system_2 = require("./file-system");
71
- Object.defineProperty(exports, "nodeFileSystem", { enumerable: true, get: function () { return file_system_2.nodeFileSystem; } });
72
- var logger_2 = require("./logger");
73
- Object.defineProperty(exports, "ConsoleLogger", { enumerable: true, get: function () { return logger_2.ConsoleLogger; } });
74
- Object.defineProperty(exports, "noopLogger", { enumerable: true, get: function () { return logger_2.noopLogger; } });
75
- var pipeline_2 = require("./pipeline");
76
- Object.defineProperty(exports, "ContractsPipeline", { enumerable: true, get: function () { return pipeline_2.ContractsPipeline; } });
77
- async function processContext(options) {
78
- const { contextName, sourceDir, outputDir, pathAliasRewrites, tsconfigPath, responseNamingConventions, removeDecorators, messageTypes, fileSystem = file_system_1.nodeFileSystem, logger = logger_1.noopLogger, } = options;
79
- const pipeline = await pipeline_1.ContractsPipeline.create({
80
- tsconfigPath,
81
- responseNamingConventions,
1
+ export { PublicCommand, PublicEvent, PublicQuery } from '@hexaijs/contracts/decorators';
2
+ import { glob } from 'glob';
3
+ import { readFile, readdir, writeFile, mkdir, access, stat } from 'fs/promises';
4
+ import { constants } from 'fs';
5
+ import * as ts6 from 'typescript';
6
+ import ts6__default from 'typescript';
7
+ import * as path3 from 'path';
8
+ import path3__default, { resolve, dirname, join, relative, basename } from 'path';
9
+ import { minimatch } from 'minimatch';
10
+ import { fileURLToPath } from 'url';
11
+
12
+ // src/domain/types.ts
13
+ var DEFAULT_DECORATOR_NAMES = {
14
+ event: "PublicEvent",
15
+ command: "PublicCommand",
16
+ query: "PublicQuery"
17
+ };
18
+ function mergeDecoratorNames(partial) {
19
+ return {
20
+ ...DEFAULT_DECORATOR_NAMES,
21
+ ...partial
22
+ };
23
+ }
24
+ function isPrimitiveType(type) {
25
+ return type.kind === "primitive";
26
+ }
27
+ function isArrayType(type) {
28
+ return type.kind === "array";
29
+ }
30
+ function isObjectType(type) {
31
+ return type.kind === "object";
32
+ }
33
+ function isUnionType(type) {
34
+ return type.kind === "union";
35
+ }
36
+ function isIntersectionType(type) {
37
+ return type.kind === "intersection";
38
+ }
39
+ function isReferenceType(type) {
40
+ return type.kind === "reference";
41
+ }
42
+ function isLiteralType(type) {
43
+ return type.kind === "literal";
44
+ }
45
+ function isTupleType(type) {
46
+ return type.kind === "tuple";
47
+ }
48
+ function isFunctionType(type) {
49
+ return type.kind === "function";
50
+ }
51
+ function isDomainEvent(message) {
52
+ return message.messageType === "event";
53
+ }
54
+ function isCommand(message) {
55
+ return message.messageType === "command";
56
+ }
57
+
58
+ // src/errors.ts
59
+ var MessageParserError = class extends Error {
60
+ constructor(message, options) {
61
+ super(message, options);
62
+ this.name = "MessageParserError";
63
+ }
64
+ };
65
+ var ConfigurationError = class extends MessageParserError {
66
+ constructor(message, options) {
67
+ super(message, options);
68
+ this.name = "ConfigurationError";
69
+ }
70
+ };
71
+ var ConfigLoadError = class extends ConfigurationError {
72
+ constructor(message, options) {
73
+ super(message, options);
74
+ this.name = "ConfigLoadError";
75
+ }
76
+ };
77
+ var FileSystemError = class extends MessageParserError {
78
+ path;
79
+ constructor(message, path5, options) {
80
+ super(message, options);
81
+ this.name = "FileSystemError";
82
+ this.path = path5;
83
+ }
84
+ };
85
+ var FileNotFoundError = class extends FileSystemError {
86
+ constructor(path5, options) {
87
+ super(`File not found: ${path5}`, path5, options);
88
+ this.name = "FileNotFoundError";
89
+ }
90
+ };
91
+ var FileReadError = class extends FileSystemError {
92
+ constructor(path5, options) {
93
+ super(`Failed to read file: ${path5}`, path5, options);
94
+ this.name = "FileReadError";
95
+ }
96
+ };
97
+ var FileWriteError = class extends FileSystemError {
98
+ constructor(path5, options) {
99
+ super(`Failed to write file: ${path5}`, path5, options);
100
+ this.name = "FileWriteError";
101
+ }
102
+ };
103
+ var ParseError = class extends MessageParserError {
104
+ constructor(message, options) {
105
+ super(message, options);
106
+ this.name = "ParseError";
107
+ }
108
+ };
109
+ var JsonParseError = class extends ParseError {
110
+ filePath;
111
+ constructor(message, filePath, options) {
112
+ const fullMessage = filePath ? `Failed to parse JSON in ${filePath}: ${message}` : `Failed to parse JSON: ${message}`;
113
+ super(fullMessage, options);
114
+ this.name = "JsonParseError";
115
+ this.filePath = filePath;
116
+ }
117
+ };
118
+ var ResolutionError = class extends MessageParserError {
119
+ constructor(message, options) {
120
+ super(message, options);
121
+ this.name = "ResolutionError";
122
+ }
123
+ };
124
+ var ModuleResolutionError = class extends ResolutionError {
125
+ moduleSpecifier;
126
+ fromFile;
127
+ constructor(moduleSpecifier, fromFile, options) {
128
+ super(
129
+ `Failed to resolve module '${moduleSpecifier}' from '${fromFile}'`,
130
+ options
131
+ );
132
+ this.name = "ModuleResolutionError";
133
+ this.moduleSpecifier = moduleSpecifier;
134
+ this.fromFile = fromFile;
135
+ }
136
+ };
137
+ var NodeFileSystem = class {
138
+ async readFile(path5) {
139
+ return readFile(path5, "utf-8");
140
+ }
141
+ async readdir(path5) {
142
+ return readdir(path5);
143
+ }
144
+ async writeFile(path5, content) {
145
+ await writeFile(path5, content);
146
+ }
147
+ async mkdir(path5, options) {
148
+ await mkdir(path5, options);
149
+ }
150
+ async exists(path5) {
151
+ try {
152
+ await access(path5, constants.F_OK);
153
+ return true;
154
+ } catch {
155
+ return false;
156
+ }
157
+ }
158
+ async stat(path5) {
159
+ return stat(path5);
160
+ }
161
+ };
162
+ var nodeFileSystem = new NodeFileSystem();
163
+
164
+ // src/scanner.ts
165
+ var DEFAULT_EXCLUDE_PATTERNS = [
166
+ "**/node_modules/**",
167
+ "**/dist/**",
168
+ "**/*.d.ts",
169
+ "**/*.test.ts",
170
+ "**/*.spec.ts"
171
+ ];
172
+ var Scanner = class {
173
+ exclude;
174
+ fs;
175
+ decoratorPatterns;
176
+ constructor(options = {}) {
177
+ this.exclude = options.exclude ?? DEFAULT_EXCLUDE_PATTERNS;
178
+ this.fs = options.fileSystem ?? nodeFileSystem;
179
+ const names = mergeDecoratorNames(options.decoratorNames);
180
+ const messageTypes = options.messageTypes ?? ["event", "command", "query"];
181
+ this.decoratorPatterns = messageTypes.map((type) => {
182
+ const decoratorName = names[type];
183
+ return `@${decoratorName}(`;
184
+ });
185
+ }
186
+ async scan(sourceDir) {
187
+ const files = await glob(`${sourceDir}/**/*.ts`, {
188
+ ignore: this.exclude
189
+ });
190
+ const result = [];
191
+ for (const file of files) {
192
+ let content;
193
+ try {
194
+ content = await this.fs.readFile(file);
195
+ } catch (error) {
196
+ throw new FileReadError(file, { cause: error });
197
+ }
198
+ if (this.containsPublicDecorator(content)) {
199
+ result.push(file);
200
+ }
201
+ }
202
+ return result;
203
+ }
204
+ containsPublicDecorator(content) {
205
+ return this.decoratorPatterns.some((pattern) => content.includes(pattern));
206
+ }
207
+ };
208
+ function parseTypeNode(typeNode) {
209
+ if (typeNode.kind === ts6.SyntaxKind.StringKeyword) {
210
+ return { kind: "primitive", name: "string" };
211
+ }
212
+ if (typeNode.kind === ts6.SyntaxKind.NumberKeyword) {
213
+ return { kind: "primitive", name: "number" };
214
+ }
215
+ if (typeNode.kind === ts6.SyntaxKind.BooleanKeyword) {
216
+ return { kind: "primitive", name: "boolean" };
217
+ }
218
+ if (ts6.isArrayTypeNode(typeNode)) {
219
+ const elementType = parseTypeNode(typeNode.elementType);
220
+ return { kind: "array", elementType };
221
+ }
222
+ if (ts6.isTypeReferenceNode(typeNode)) {
223
+ const name = typeNode.typeName.getText();
224
+ const typeArguments = typeNode.typeArguments ? typeNode.typeArguments.map((t) => parseTypeNode(t)) : void 0;
225
+ return { kind: "reference", name, typeArguments };
226
+ }
227
+ if (ts6.isIntersectionTypeNode(typeNode)) {
228
+ const types = typeNode.types.map((t) => parseTypeNode(t));
229
+ return { kind: "intersection", types };
230
+ }
231
+ if (ts6.isUnionTypeNode(typeNode)) {
232
+ const types = typeNode.types.map((t) => parseTypeNode(t));
233
+ return { kind: "union", types };
234
+ }
235
+ if (ts6.isTypeLiteralNode(typeNode)) {
236
+ const fields = extractFieldsFromMembers(typeNode.members);
237
+ return { kind: "object", fields };
238
+ }
239
+ return { kind: "reference", name: typeNode.getText() };
240
+ }
241
+ function extractFieldsFromMembers(members) {
242
+ const fields = [];
243
+ for (const member of members) {
244
+ if (ts6.isPropertySignature(member) && member.name) {
245
+ const fieldName = member.name.getText();
246
+ const fieldType = member.type ? parseTypeNode(member.type) : { kind: "primitive", name: "any" };
247
+ const optional = !!member.questionToken;
248
+ const readonly = member.modifiers?.some(
249
+ (m) => m.kind === ts6.SyntaxKind.ReadonlyKeyword
250
+ ) ?? false;
251
+ fields.push({
252
+ name: fieldName,
253
+ type: fieldType,
254
+ optional,
255
+ readonly
256
+ });
257
+ }
258
+ }
259
+ return fields;
260
+ }
261
+ function hasDecorator(node, decoratorName) {
262
+ const decorators = ts6.getDecorators(node);
263
+ if (!decorators) return false;
264
+ return decorators.some((decorator) => {
265
+ if (ts6.isCallExpression(decorator.expression)) {
266
+ const expr = decorator.expression.expression;
267
+ if (ts6.isIdentifier(expr)) {
268
+ return expr.text === decoratorName;
269
+ }
270
+ }
271
+ return false;
272
+ });
273
+ }
274
+ function getDecoratorOptions(node, decoratorName) {
275
+ const decorators = ts6.getDecorators(node);
276
+ if (!decorators) return void 0;
277
+ for (const decorator of decorators) {
278
+ if (!ts6.isCallExpression(decorator.expression)) continue;
279
+ const expr = decorator.expression.expression;
280
+ if (!ts6.isIdentifier(expr) || expr.text !== decoratorName) continue;
281
+ const args = decorator.expression.arguments;
282
+ if (args.length === 0) return {};
283
+ const firstArg = args[0];
284
+ if (!ts6.isObjectLiteralExpression(firstArg)) return {};
285
+ const options = {};
286
+ for (const prop of firstArg.properties) {
287
+ if (!ts6.isPropertyAssignment(prop)) continue;
288
+ if (!ts6.isIdentifier(prop.name)) continue;
289
+ const propName = prop.name.text;
290
+ if (propName === "response" && ts6.isStringLiteral(prop.initializer)) {
291
+ options.response = prop.initializer.text;
292
+ } else if (propName === "context" && ts6.isStringLiteral(prop.initializer)) {
293
+ options.context = prop.initializer.text;
294
+ }
295
+ }
296
+ return options;
297
+ }
298
+ return void 0;
299
+ }
300
+ function hasExportModifier(node) {
301
+ const modifiers = ts6.canHaveModifiers(node) ? ts6.getModifiers(node) : void 0;
302
+ return modifiers?.some((m) => m.kind === ts6.SyntaxKind.ExportKeyword) ?? false;
303
+ }
304
+ function extractClassSourceText(node, sourceCode) {
305
+ const fullStart = node.getFullStart();
306
+ const end = node.getEnd();
307
+ let sourceText = sourceCode.slice(fullStart, end);
308
+ sourceText = sourceText.replace(/^\s*\n/, "");
309
+ return sourceText;
310
+ }
311
+ function getBaseClassName(node) {
312
+ if (!node.heritageClauses) return void 0;
313
+ for (const clause of node.heritageClauses) {
314
+ if (clause.token === ts6.SyntaxKind.ExtendsKeyword) {
315
+ const firstType = clause.types[0];
316
+ if (firstType && ts6.isExpressionWithTypeArguments(firstType)) {
317
+ const expr = firstType.expression;
318
+ if (ts6.isIdentifier(expr)) {
319
+ return expr.text;
320
+ }
321
+ }
322
+ }
323
+ }
324
+ return void 0;
325
+ }
326
+ function isExternalModule(moduleSpecifier) {
327
+ return !moduleSpecifier.startsWith(".");
328
+ }
329
+ function extractImports(sourceFile) {
330
+ const imports = [];
331
+ ts6.forEachChild(sourceFile, (node) => {
332
+ if (ts6.isImportDeclaration(node) && node.importClause) {
333
+ const moduleSpecifier = node.moduleSpecifier.text;
334
+ const isExternal = isExternalModule(moduleSpecifier);
335
+ const isTypeOnly = node.importClause.isTypeOnly ?? false;
336
+ const importedNames = extractImportedNames(node.importClause);
337
+ if (importedNames.length > 0) {
338
+ imports.push({
339
+ names: importedNames,
340
+ source: moduleSpecifier,
341
+ isTypeOnly,
342
+ isExternal
343
+ });
344
+ }
345
+ }
346
+ });
347
+ return imports;
348
+ }
349
+ function extractImportedNames(importClause) {
350
+ const names = [];
351
+ if (importClause.name) {
352
+ names.push(importClause.name.text);
353
+ }
354
+ if (importClause.namedBindings) {
355
+ if (ts6.isNamedImports(importClause.namedBindings)) {
356
+ for (const element of importClause.namedBindings.elements) {
357
+ names.push(element.name.text);
358
+ }
359
+ }
360
+ }
361
+ return names;
362
+ }
363
+
364
+ // src/parser.ts
365
+ var PAYLOAD_TYPE_ARGUMENT_INDEX = 0;
366
+ function buildDecoratorMappings(decoratorNames) {
367
+ return [
368
+ { decorator: decoratorNames.event, messageType: "event" },
369
+ { decorator: decoratorNames.command, messageType: "command" },
370
+ { decorator: decoratorNames.query, messageType: "query" }
371
+ ];
372
+ }
373
+ function extractTypeParameterNames(typeParameters) {
374
+ if (!typeParameters) return void 0;
375
+ return typeParameters.map((tp) => tp.name.text);
376
+ }
377
+ var Parser = class {
378
+ decoratorMappings;
379
+ responseNamingConventions;
380
+ messageTypes;
381
+ constructor(options = {}) {
382
+ const names = mergeDecoratorNames(options.decoratorNames);
383
+ this.decoratorMappings = buildDecoratorMappings(names);
384
+ this.responseNamingConventions = options.responseNamingConventions ?? [];
385
+ this.messageTypes = options.messageTypes;
386
+ }
387
+ parse(sourceCode, sourceFileInfo) {
388
+ const tsSourceFile = ts6__default.createSourceFile(
389
+ sourceFileInfo.absolutePath,
390
+ sourceCode,
391
+ ts6__default.ScriptTarget.Latest,
392
+ true
393
+ );
394
+ const events = [];
395
+ const commands = [];
396
+ const queries = [];
397
+ const typeDefinitions = [];
398
+ const messageCollectors = {
399
+ event: (m) => events.push(m),
400
+ command: (m) => commands.push(m),
401
+ query: (m) => queries.push(m)
402
+ };
403
+ const visit = (node) => {
404
+ if (ts6__default.isClassDeclaration(node) && node.name) {
405
+ this.collectMessagesFromClass(node, sourceCode, tsSourceFile, sourceFileInfo, messageCollectors);
406
+ }
407
+ if (ts6__default.isTypeAliasDeclaration(node) && node.name) {
408
+ typeDefinitions.push(this.extractTypeDefinition(node, sourceFileInfo));
409
+ }
410
+ if (ts6__default.isInterfaceDeclaration(node) && node.name) {
411
+ typeDefinitions.push(this.extractInterfaceDefinition(node, sourceFileInfo));
412
+ }
413
+ ts6__default.forEachChild(node, visit);
414
+ };
415
+ visit(tsSourceFile);
416
+ this.applyNamingConventionMatching(commands, typeDefinitions);
417
+ this.applyNamingConventionMatching(queries, typeDefinitions);
418
+ return { events, commands, queries, typeDefinitions };
419
+ }
420
+ collectMessagesFromClass(node, sourceCode, tsSourceFile, sourceFileInfo, collectors) {
421
+ for (const { decorator, messageType } of this.decoratorMappings) {
422
+ if (!hasDecorator(node, decorator)) continue;
423
+ if (this.messageTypes && !this.messageTypes.includes(messageType)) continue;
424
+ const message = this.buildMessage(node, sourceCode, tsSourceFile, sourceFileInfo, messageType, decorator);
425
+ collectors[messageType](message);
426
+ }
427
+ }
428
+ applyNamingConventionMatching(messages, typeDefinitions) {
429
+ for (const message of messages) {
430
+ const hasExplicitResultType = Boolean(message.resultType);
431
+ if (hasExplicitResultType) continue;
432
+ const matchedTypeName = this.findMatchingResponseType(message.name, typeDefinitions);
433
+ if (matchedTypeName) {
434
+ message.resultType = {
435
+ kind: "reference",
436
+ name: matchedTypeName
437
+ };
438
+ }
439
+ }
440
+ }
441
+ findMatchingResponseType(messageName, typeDefinitions) {
442
+ for (const convention of this.responseNamingConventions) {
443
+ const hasSuffix = messageName.endsWith(convention.messageSuffix);
444
+ if (!hasSuffix) continue;
445
+ const messagePrefix = messageName.slice(0, -convention.messageSuffix.length);
446
+ const expectedResponseName = messagePrefix + convention.responseSuffix;
447
+ const matchingType = typeDefinitions.find((t) => t.name === expectedResponseName);
448
+ if (matchingType) return matchingType.name;
449
+ }
450
+ return void 0;
451
+ }
452
+ buildMessage(node, sourceCode, tsSourceFile, sourceFileInfo, messageType, decoratorName) {
453
+ const extracted = this.extractMessageDetails(node, sourceCode, tsSourceFile);
454
+ const decoratorOptions = getDecoratorOptions(node, decoratorName);
455
+ const baseMessage = {
456
+ name: node.name.text,
457
+ messageType,
458
+ sourceFile: sourceFileInfo,
459
+ fields: extracted.fields,
460
+ payloadType: extracted.payloadType,
461
+ sourceText: extracted.sourceText,
462
+ imports: extracted.imports,
463
+ baseClass: extracted.baseClass
464
+ };
465
+ const explicitResponseName = decoratorOptions?.response;
466
+ const supportsResultType = messageType === "command" || messageType === "query";
467
+ if (!supportsResultType || !explicitResponseName) {
468
+ return baseMessage;
469
+ }
470
+ return {
471
+ ...baseMessage,
472
+ resultType: {
473
+ kind: "reference",
474
+ name: explicitResponseName
475
+ }
476
+ };
477
+ }
478
+ extractMessageDetails(node, sourceCode, tsSourceFile) {
479
+ const { fields, payloadType } = this.extractPayload(node);
480
+ const sourceText = extractClassSourceText(node, sourceCode);
481
+ const imports = extractImports(tsSourceFile);
482
+ const baseClass = getBaseClassName(node);
483
+ return {
484
+ fields,
485
+ payloadType,
486
+ sourceText,
487
+ imports,
488
+ baseClass
489
+ };
490
+ }
491
+ extractPayload(node) {
492
+ const emptyPayload = { fields: [] };
493
+ if (!node.heritageClauses) return emptyPayload;
494
+ const extendsClause = node.heritageClauses.find(
495
+ (clause) => clause.token === ts6__default.SyntaxKind.ExtendsKeyword
496
+ );
497
+ if (!extendsClause) return emptyPayload;
498
+ for (const type of extendsClause.types) {
499
+ const typeArgs = type.typeArguments;
500
+ if (!typeArgs || typeArgs.length === 0) continue;
501
+ const payloadTypeArg = typeArgs[PAYLOAD_TYPE_ARGUMENT_INDEX];
502
+ const extractedPayload = this.parsePayloadTypeArgument(payloadTypeArg);
503
+ if (extractedPayload) return extractedPayload;
504
+ }
505
+ return emptyPayload;
506
+ }
507
+ parsePayloadTypeArgument(typeArg) {
508
+ if (ts6__default.isTypeReferenceNode(typeArg) && ts6__default.isIdentifier(typeArg.typeName)) {
509
+ return {
510
+ fields: [],
511
+ payloadType: parseTypeNode(typeArg)
512
+ };
513
+ }
514
+ if (ts6__default.isTypeLiteralNode(typeArg)) {
515
+ return {
516
+ fields: extractFieldsFromMembers(typeArg.members)
517
+ };
518
+ }
519
+ if (ts6__default.isIntersectionTypeNode(typeArg)) {
520
+ const parsedType = parseTypeNode(typeArg);
521
+ return {
522
+ fields: this.flattenIntersectionToFields(parsedType),
523
+ payloadType: parsedType
524
+ };
525
+ }
526
+ return void 0;
527
+ }
528
+ flattenIntersectionToFields(intersection) {
529
+ const fields = [];
530
+ for (const type of intersection.types) {
531
+ if (type.kind === "object") {
532
+ fields.push(...type.fields);
533
+ }
534
+ }
535
+ return fields;
536
+ }
537
+ extractTypeDefinition(node, sourceFileInfo) {
538
+ return this.buildTypeDefinition(
539
+ node.name.text,
540
+ "type",
541
+ sourceFileInfo,
542
+ parseTypeNode(node.type),
543
+ node.typeParameters,
544
+ node
545
+ );
546
+ }
547
+ extractInterfaceDefinition(node, sourceFileInfo) {
548
+ const body = {
549
+ kind: "object",
550
+ fields: extractFieldsFromMembers(node.members)
551
+ };
552
+ return this.buildTypeDefinition(
553
+ node.name.text,
554
+ "interface",
555
+ sourceFileInfo,
556
+ body,
557
+ node.typeParameters,
558
+ node
559
+ );
560
+ }
561
+ buildTypeDefinition(name, kind, sourceFile, body, typeParameters, node) {
562
+ return {
563
+ name,
564
+ kind,
565
+ sourceFile,
566
+ body,
567
+ typeParameters: extractTypeParameterNames(typeParameters),
568
+ exported: hasExportModifier(node)
569
+ };
570
+ }
571
+ };
572
+ var TYPESCRIPT_EXTENSIONS = [".ts", ".tsx"];
573
+ var INDEX_FILE = "index.ts";
574
+ var FileGraphResolver = class _FileGraphResolver {
575
+ contextConfig;
576
+ fs;
577
+ excludeDependencies;
578
+ constructor(contextConfig, fileSystem, excludeDependencies) {
579
+ this.contextConfig = contextConfig;
580
+ this.fs = fileSystem;
581
+ this.excludeDependencies = excludeDependencies;
582
+ }
583
+ static create(options) {
584
+ const fs = options.fileSystem ?? nodeFileSystem;
585
+ const excludeDependencies = options.excludeDependencies ?? [];
586
+ return new _FileGraphResolver(options.contextConfig, fs, excludeDependencies);
587
+ }
588
+ async buildGraph(entryPoints, sourceRoot) {
589
+ const nodes = /* @__PURE__ */ new Map();
590
+ const entryPointSet = new Set(entryPoints);
591
+ const excludedPaths = /* @__PURE__ */ new Set();
592
+ const visited = /* @__PURE__ */ new Set();
593
+ const queue = [...entryPoints];
594
+ while (queue.length > 0) {
595
+ const filePath = queue.shift();
596
+ if (visited.has(filePath)) {
597
+ continue;
598
+ }
599
+ visited.add(filePath);
600
+ const imports = await this.extractImports(filePath);
601
+ const node = {
602
+ absolutePath: filePath,
603
+ relativePath: path3__default.relative(sourceRoot, filePath),
604
+ imports,
605
+ isEntryPoint: entryPointSet.has(filePath)
606
+ };
607
+ nodes.set(filePath, node);
608
+ this.queueUnvisitedLocalDependencies(imports, visited, queue, excludedPaths);
609
+ }
610
+ return {
611
+ nodes,
612
+ entryPoints: entryPointSet,
613
+ excludedPaths
614
+ };
615
+ }
616
+ async extractImports(filePath) {
617
+ const sourceCode = await this.readSourceFile(filePath);
618
+ const sourceFile = ts6__default.createSourceFile(
619
+ filePath,
620
+ sourceCode,
621
+ ts6__default.ScriptTarget.Latest,
622
+ true
623
+ );
624
+ const imports = [];
625
+ for (const node of sourceFile.statements) {
626
+ const moduleSpecifier = this.extractModuleSpecifier(node);
627
+ if (!moduleSpecifier) {
628
+ continue;
629
+ }
630
+ const resolution = await this.resolveModule(moduleSpecifier, filePath);
631
+ const importedNames = this.extractImportedNamesFromNode(node);
632
+ imports.push({
633
+ moduleSpecifier,
634
+ resolvedPath: resolution.resolvedPath,
635
+ isExternal: resolution.isExternal,
636
+ importedNames
637
+ });
638
+ }
639
+ return imports;
640
+ }
641
+ async readSourceFile(filePath) {
642
+ try {
643
+ return await this.fs.readFile(filePath);
644
+ } catch (error) {
645
+ throw new FileReadError(filePath, { cause: error });
646
+ }
647
+ }
648
+ extractModuleSpecifier(node) {
649
+ if (ts6__default.isImportDeclaration(node)) {
650
+ return node.moduleSpecifier.text;
651
+ }
652
+ if (ts6__default.isExportDeclaration(node) && node.moduleSpecifier) {
653
+ return node.moduleSpecifier.text;
654
+ }
655
+ return null;
656
+ }
657
+ extractImportedNamesFromNode(node) {
658
+ if (ts6__default.isImportDeclaration(node) && node.importClause) {
659
+ return extractImportedNames(node.importClause);
660
+ }
661
+ return [];
662
+ }
663
+ async resolveModule(moduleSpecifier, fromFile) {
664
+ if (moduleSpecifier.startsWith(".")) {
665
+ const resolvedPath = await this.resolveRelativeModule(
666
+ moduleSpecifier,
667
+ fromFile
668
+ );
669
+ return { resolvedPath, isExternal: false };
670
+ }
671
+ return this.contextConfig.resolvePath(moduleSpecifier);
672
+ }
673
+ queueUnvisitedLocalDependencies(imports, visited, queue, excludedPaths) {
674
+ const localImports = imports.filter(
675
+ (importInfo) => !importInfo.isExternal && importInfo.resolvedPath !== null
676
+ );
677
+ for (const importInfo of localImports) {
678
+ const resolvedPath = importInfo.resolvedPath;
679
+ if (this.shouldExclude(resolvedPath)) {
680
+ excludedPaths.add(resolvedPath);
681
+ continue;
682
+ }
683
+ if (!visited.has(resolvedPath)) {
684
+ queue.push(resolvedPath);
685
+ }
686
+ }
687
+ }
688
+ shouldExclude(filePath) {
689
+ if (this.excludeDependencies.length === 0) {
690
+ return false;
691
+ }
692
+ return this.excludeDependencies.some(
693
+ (pattern) => minimatch(filePath, pattern, { matchBase: true })
694
+ );
695
+ }
696
+ async resolveRelativeModule(moduleSpecifier, fromFile) {
697
+ const dir = path3__default.dirname(fromFile);
698
+ const basePath = path3__default.resolve(dir, moduleSpecifier);
699
+ return this.tryResolveWithExtensions(basePath);
700
+ }
701
+ async tryResolveWithExtensions(basePath) {
702
+ const extensionCandidates = TYPESCRIPT_EXTENSIONS.map((ext) => basePath + ext);
703
+ const indexCandidate = path3__default.join(basePath, INDEX_FILE);
704
+ const candidates = [...extensionCandidates, indexCandidate];
705
+ for (const candidate of candidates) {
706
+ if (await this.fs.exists(candidate)) {
707
+ return candidate;
708
+ }
709
+ }
710
+ return null;
711
+ }
712
+ };
713
+ var CONTRACTS_GENERATOR_MODULE = "@hexaijs/plugin-contracts-generator";
714
+ var CONTRACT_DECORATORS = /* @__PURE__ */ new Set([
715
+ "PublicCommand",
716
+ "PublicEvent",
717
+ "PublicQuery"
718
+ ]);
719
+ var TS_EXTENSION_PATTERN = /\.ts$/;
720
+ var REQUEST_SUFFIX = "Request";
721
+ var QUERY_SUFFIX = "Query";
722
+ var FileCopier = class {
723
+ fs;
724
+ constructor(options = {}) {
725
+ this.fs = options.fileSystem ?? nodeFileSystem;
726
+ }
727
+ async copyFiles(options) {
728
+ const {
729
+ sourceRoot,
730
+ outputDir,
731
+ fileGraph,
732
+ pathAliasRewrites,
733
+ removeDecorators,
734
+ responseTypesToExport,
735
+ messageTypes,
736
+ decoratorNames
737
+ } = options;
738
+ const copiedFiles = [];
739
+ const rewrittenImports = /* @__PURE__ */ new Map();
740
+ const { entryContents, usedLocalImports } = await this.preprocessEntryFiles(
741
+ fileGraph,
742
+ messageTypes,
743
+ decoratorNames,
744
+ sourceRoot
745
+ );
746
+ this.expandTransitiveDependencies(usedLocalImports, fileGraph);
747
+ for (const node of fileGraph.nodes.values()) {
748
+ const content = await this.resolveNodeContent(
749
+ node,
750
+ entryContents,
751
+ usedLocalImports,
752
+ messageTypes
753
+ );
754
+ if (content === null) {
755
+ continue;
756
+ }
757
+ const transformedContent = this.applyTransformations(
758
+ content,
759
+ node,
760
+ fileGraph,
761
+ sourceRoot,
762
+ removeDecorators,
763
+ responseTypesToExport,
764
+ pathAliasRewrites
765
+ );
766
+ const outputPath = await this.writeOutputFile(
767
+ outputDir,
768
+ node.relativePath,
769
+ transformedContent.content
770
+ );
771
+ copiedFiles.push(outputPath);
772
+ if (transformedContent.rewrites.length > 0) {
773
+ rewrittenImports.set(outputPath, transformedContent.rewrites);
774
+ }
775
+ }
776
+ return { copiedFiles, rewrittenImports };
777
+ }
778
+ async preprocessEntryFiles(fileGraph, messageTypes, decoratorNames, sourceRoot) {
779
+ const entryContents = /* @__PURE__ */ new Map();
780
+ const usedLocalImports = /* @__PURE__ */ new Set();
781
+ if (!messageTypes?.length) {
782
+ return { entryContents, usedLocalImports };
783
+ }
784
+ for (const node of fileGraph.nodes.values()) {
785
+ if (!node.isEntryPoint) {
786
+ continue;
787
+ }
788
+ const rawContent = await this.readFileContent(node.absolutePath);
789
+ const { content: extractedContent, usedModuleSpecifiers } = this.extractSymbolsFromEntry(
790
+ rawContent,
791
+ node.absolutePath,
82
792
  messageTypes,
793
+ decoratorNames
794
+ );
795
+ entryContents.set(node.absolutePath, extractedContent);
796
+ for (const specifier of usedModuleSpecifiers) {
797
+ const importInfo = node.imports.find(
798
+ (i) => i.moduleSpecifier === specifier
799
+ );
800
+ if (importInfo?.resolvedPath && !importInfo.isExternal) {
801
+ usedLocalImports.add(importInfo.resolvedPath);
802
+ }
803
+ }
804
+ }
805
+ return { entryContents, usedLocalImports };
806
+ }
807
+ expandTransitiveDependencies(usedLocalImports, fileGraph) {
808
+ let changed = true;
809
+ while (changed) {
810
+ changed = false;
811
+ for (const importPath of usedLocalImports) {
812
+ const node = fileGraph.nodes.get(importPath);
813
+ if (!node) continue;
814
+ for (const imp of node.imports) {
815
+ const isUnusedLocalImport = imp.resolvedPath && !imp.isExternal && !usedLocalImports.has(imp.resolvedPath);
816
+ if (isUnusedLocalImport) {
817
+ usedLocalImports.add(imp.resolvedPath);
818
+ changed = true;
819
+ }
820
+ }
821
+ }
822
+ }
823
+ }
824
+ async resolveNodeContent(node, entryContents, usedLocalImports, messageTypes) {
825
+ if (node.isEntryPoint && messageTypes?.length) {
826
+ return entryContents.get(node.absolutePath) ?? await this.readFileContent(node.absolutePath);
827
+ }
828
+ const isUnusedDependency = messageTypes?.length && !usedLocalImports.has(node.absolutePath);
829
+ if (isUnusedDependency) {
830
+ return null;
831
+ }
832
+ return await this.readFileContent(node.absolutePath);
833
+ }
834
+ applyTransformations(content, node, fileGraph, sourceRoot, removeDecorators, responseTypesToExport, pathAliasRewrites) {
835
+ const rewrites = [];
836
+ let transformedContent = content;
837
+ transformedContent = this.processExcludedImports(
838
+ transformedContent,
839
+ node,
840
+ fileGraph.excludedPaths,
841
+ rewrites
842
+ );
843
+ transformedContent = this.processDecoratorRemoval(
844
+ transformedContent,
845
+ node.absolutePath,
846
+ removeDecorators,
847
+ rewrites
848
+ );
849
+ transformedContent = this.processTypeExports(
850
+ transformedContent,
851
+ node.absolutePath,
852
+ responseTypesToExport?.get(node.absolutePath),
853
+ rewrites
854
+ );
855
+ transformedContent = this.processInternalPathAliases(
856
+ transformedContent,
857
+ node,
858
+ fileGraph,
859
+ sourceRoot,
860
+ rewrites
861
+ );
862
+ transformedContent = this.processExternalPathAliases(
863
+ transformedContent,
864
+ pathAliasRewrites,
865
+ rewrites
866
+ );
867
+ return { content: transformedContent, rewrites };
868
+ }
869
+ generateBarrelExport(copiedFiles, outputDir) {
870
+ const lines = [];
871
+ for (const filePath of copiedFiles) {
872
+ const relativePath = path3.relative(outputDir, filePath);
873
+ lines.push(this.createExportStatement(relativePath));
874
+ }
875
+ return lines.join("\n");
876
+ }
877
+ async readFileContent(absolutePath) {
878
+ try {
879
+ return await this.fs.readFile(absolutePath);
880
+ } catch (error) {
881
+ throw new FileReadError(absolutePath, { cause: error });
882
+ }
883
+ }
884
+ async writeOutputFile(outputDir, relativePath, content) {
885
+ const outputPath = path3.join(outputDir, relativePath);
886
+ const outputDirPath = path3.dirname(outputPath);
887
+ await this.fs.mkdir(outputDirPath, { recursive: true });
888
+ try {
889
+ await this.fs.writeFile(outputPath, content);
890
+ } catch (error) {
891
+ throw new FileWriteError(outputPath, { cause: error });
892
+ }
893
+ return outputPath;
894
+ }
895
+ processExcludedImports(content, node, excludedPaths, rewrites) {
896
+ if (excludedPaths.size === 0) {
897
+ return content;
898
+ }
899
+ const excludedResult = this.removeExcludedImports(
900
+ content,
901
+ node,
902
+ excludedPaths
903
+ );
904
+ rewrites.push(...excludedResult.changes);
905
+ return excludedResult.content;
906
+ }
907
+ processDecoratorRemoval(content, filePath, removeDecorators, rewrites) {
908
+ if (!removeDecorators) {
909
+ return content;
910
+ }
911
+ const decoratorResult = this.removeContractDecorators(
912
+ content,
913
+ filePath
914
+ );
915
+ rewrites.push(...decoratorResult.changes);
916
+ return decoratorResult.content;
917
+ }
918
+ processTypeExports(content, filePath, typesToExport, rewrites) {
919
+ if (!typesToExport || typesToExport.length === 0) {
920
+ return content;
921
+ }
922
+ const exportResult = this.addExportToTypes(
923
+ content,
924
+ filePath,
925
+ typesToExport
926
+ );
927
+ rewrites.push(...exportResult.changes);
928
+ return exportResult.content;
929
+ }
930
+ processInternalPathAliases(content, node, fileGraph, sourceRoot, rewrites) {
931
+ const internalResult = this.rewriteInternalPathAliases(
932
+ content,
933
+ node,
934
+ fileGraph,
935
+ sourceRoot
936
+ );
937
+ rewrites.push(...internalResult.rewrites);
938
+ return internalResult.content;
939
+ }
940
+ processExternalPathAliases(content, pathAliasRewrites, rewrites) {
941
+ if (!pathAliasRewrites) {
942
+ return content;
943
+ }
944
+ const aliasResult = this.applyPathAliasRewrites(
945
+ content,
946
+ pathAliasRewrites
947
+ );
948
+ rewrites.push(...aliasResult.rewrites);
949
+ return aliasResult.content;
950
+ }
951
+ createExportStatement(relativePath) {
952
+ const exportPath = "./" + relativePath.replace(TS_EXTENSION_PATTERN, "");
953
+ return `export * from '${exportPath}'`;
954
+ }
955
+ rewriteInternalPathAliases(content, node, fileGraph, sourceRoot) {
956
+ let transformedContent = content;
957
+ const appliedRewrites = [];
958
+ for (const importInfo of node.imports) {
959
+ if (this.isExternalOrRelativeImport(importInfo)) {
960
+ continue;
961
+ }
962
+ const targetNode = this.resolveInternalImport(
963
+ importInfo,
964
+ fileGraph
965
+ );
966
+ if (!targetNode) {
967
+ continue;
968
+ }
969
+ const relativePath = this.computeRelativePath(
970
+ node.relativePath,
971
+ targetNode.relativePath
972
+ );
973
+ const rewriteResult = this.rewriteModuleSpecifier(
974
+ transformedContent,
975
+ importInfo.moduleSpecifier,
976
+ relativePath
977
+ );
978
+ if (rewriteResult.wasRewritten) {
979
+ transformedContent = rewriteResult.content;
980
+ appliedRewrites.push(
981
+ `${importInfo.moduleSpecifier} \u2192 ${relativePath}`
982
+ );
983
+ }
984
+ }
985
+ return { content: transformedContent, rewrites: appliedRewrites };
986
+ }
987
+ isExternalOrRelativeImport(importInfo) {
988
+ return importInfo.isExternal || importInfo.moduleSpecifier.startsWith(".");
989
+ }
990
+ resolveInternalImport(importInfo, fileGraph) {
991
+ if (!importInfo.resolvedPath || !fileGraph.nodes.has(importInfo.resolvedPath)) {
992
+ return void 0;
993
+ }
994
+ return fileGraph.nodes.get(importInfo.resolvedPath);
995
+ }
996
+ rewriteModuleSpecifier(content, originalSpecifier, newSpecifier) {
997
+ const importPattern = new RegExp(
998
+ `(from\\s+['"])${this.escapeRegex(originalSpecifier)}(['"])`,
999
+ "g"
1000
+ );
1001
+ if (!importPattern.test(content)) {
1002
+ return { content, wasRewritten: false };
1003
+ }
1004
+ const rewrittenContent = content.replace(
1005
+ importPattern,
1006
+ `$1${newSpecifier}$2`
1007
+ );
1008
+ return { content: rewrittenContent, wasRewritten: true };
1009
+ }
1010
+ computeRelativePath(fromRelative, toRelative) {
1011
+ const fromDir = path3.dirname(fromRelative);
1012
+ let relativePath = path3.relative(fromDir, toRelative);
1013
+ relativePath = relativePath.replace(TS_EXTENSION_PATTERN, "");
1014
+ if (!relativePath.startsWith(".")) {
1015
+ relativePath = "./" + relativePath;
1016
+ }
1017
+ return relativePath;
1018
+ }
1019
+ applyPathAliasRewrites(content, rewrites) {
1020
+ let transformedContent = content;
1021
+ const appliedRewrites = [];
1022
+ for (const [from, to] of rewrites) {
1023
+ if (transformedContent.includes(from)) {
1024
+ transformedContent = transformedContent.replace(
1025
+ new RegExp(this.escapeRegex(from), "g"),
1026
+ to
1027
+ );
1028
+ appliedRewrites.push(`${from} \u2192 ${to}`);
1029
+ }
1030
+ }
1031
+ return { content: transformedContent, rewrites: appliedRewrites };
1032
+ }
1033
+ escapeRegex(str) {
1034
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1035
+ }
1036
+ removeExcludedImports(content, node, excludedPaths) {
1037
+ const excludedModuleSpecifiers = this.findExcludedModuleSpecifiers(
1038
+ node,
1039
+ excludedPaths
1040
+ );
1041
+ if (excludedModuleSpecifiers.size === 0) {
1042
+ return { content, changes: [] };
1043
+ }
1044
+ const changes = Array.from(excludedModuleSpecifiers).map(
1045
+ (specifier) => `removed import: ${specifier}`
1046
+ );
1047
+ const visitorFactory = this.createExcludedImportsVisitor(
1048
+ excludedModuleSpecifiers
1049
+ );
1050
+ const transformedContent = this.transformSourceFile(
1051
+ content,
1052
+ node.absolutePath,
1053
+ visitorFactory
1054
+ );
1055
+ return { content: transformedContent, changes };
1056
+ }
1057
+ findExcludedModuleSpecifiers(node, excludedPaths) {
1058
+ const excludedModuleSpecifiers = /* @__PURE__ */ new Set();
1059
+ for (const importInfo of node.imports) {
1060
+ if (importInfo.resolvedPath && excludedPaths.has(importInfo.resolvedPath)) {
1061
+ excludedModuleSpecifiers.add(importInfo.moduleSpecifier);
1062
+ }
1063
+ }
1064
+ return excludedModuleSpecifiers;
1065
+ }
1066
+ createExcludedImportsVisitor(excludedModuleSpecifiers) {
1067
+ return (context) => {
1068
+ const visit = (node) => {
1069
+ if (this.isImportFromExcludedModule(
1070
+ node,
1071
+ excludedModuleSpecifiers
1072
+ )) {
1073
+ return void 0;
1074
+ }
1075
+ if (this.isExportFromExcludedModule(
1076
+ node,
1077
+ excludedModuleSpecifiers
1078
+ )) {
1079
+ return void 0;
1080
+ }
1081
+ return ts6.visitEachChild(node, visit, context);
1082
+ };
1083
+ return visit;
1084
+ };
1085
+ }
1086
+ isImportFromExcludedModule(node, excludedModules) {
1087
+ if (!ts6.isImportDeclaration(node)) {
1088
+ return false;
1089
+ }
1090
+ const moduleSpecifier = node.moduleSpecifier.text;
1091
+ return excludedModules.has(moduleSpecifier);
1092
+ }
1093
+ isExportFromExcludedModule(node, excludedModules) {
1094
+ if (!ts6.isExportDeclaration(node) || !node.moduleSpecifier) {
1095
+ return false;
1096
+ }
1097
+ const moduleSpecifier = node.moduleSpecifier.text;
1098
+ return excludedModules.has(moduleSpecifier);
1099
+ }
1100
+ removeContractDecorators(content, filePath) {
1101
+ const changes = [];
1102
+ const visitorFactory = this.createDecoratorRemovalVisitor(changes);
1103
+ const transformedContent = this.transformSourceFile(
1104
+ content,
1105
+ filePath,
1106
+ visitorFactory
1107
+ );
1108
+ return { content: transformedContent, changes };
1109
+ }
1110
+ createDecoratorRemovalVisitor(changes) {
1111
+ return (context) => {
1112
+ const visit = (node) => {
1113
+ if (ts6.isImportDeclaration(node)) {
1114
+ return this.processContractGeneratorImport(node, changes);
1115
+ }
1116
+ if (ts6.isClassDeclaration(node) && node.modifiers) {
1117
+ return this.removeContractDecoratorsFromClass(
1118
+ node,
1119
+ changes
1120
+ );
1121
+ }
1122
+ return ts6.visitEachChild(node, visit, context);
1123
+ };
1124
+ return visit;
1125
+ };
1126
+ }
1127
+ processContractGeneratorImport(node, changes) {
1128
+ const moduleSpecifier = node.moduleSpecifier.text;
1129
+ const isContractGeneratorModule = moduleSpecifier === CONTRACTS_GENERATOR_MODULE || moduleSpecifier.startsWith(CONTRACTS_GENERATOR_MODULE + "/");
1130
+ if (!isContractGeneratorModule) {
1131
+ return node;
1132
+ }
1133
+ const namedBindings = node.importClause?.namedBindings;
1134
+ if (!namedBindings || !ts6.isNamedImports(namedBindings)) {
1135
+ return node;
1136
+ }
1137
+ const remainingElements = namedBindings.elements.filter(
1138
+ (el) => !CONTRACT_DECORATORS.has(el.name.text)
1139
+ );
1140
+ if (remainingElements.length === 0) {
1141
+ changes.push(`removed import: ${moduleSpecifier}`);
1142
+ return void 0;
1143
+ }
1144
+ if (remainingElements.length < namedBindings.elements.length) {
1145
+ changes.push(`removed decorators from import: ${moduleSpecifier}`);
1146
+ return this.createImportWithFilteredBindings(
1147
+ node,
1148
+ remainingElements
1149
+ );
1150
+ }
1151
+ return node;
1152
+ }
1153
+ createImportWithFilteredBindings(originalImport, remainingElements) {
1154
+ const newNamedImports = ts6.factory.createNamedImports(remainingElements);
1155
+ const newImportClause = ts6.factory.createImportClause(
1156
+ originalImport.importClause.isTypeOnly,
1157
+ originalImport.importClause.name,
1158
+ newNamedImports
1159
+ );
1160
+ return ts6.factory.createImportDeclaration(
1161
+ originalImport.modifiers,
1162
+ newImportClause,
1163
+ originalImport.moduleSpecifier,
1164
+ originalImport.attributes
1165
+ );
1166
+ }
1167
+ removeContractDecoratorsFromClass(node, changes) {
1168
+ const filteredModifiers = node.modifiers.filter((modifier) => {
1169
+ if (!ts6.isDecorator(modifier)) {
1170
+ return true;
1171
+ }
1172
+ const decoratorName = this.extractDecoratorName(modifier);
1173
+ if (decoratorName && CONTRACT_DECORATORS.has(decoratorName)) {
1174
+ const decoratorSuffix = this.isDecoratorCallExpression(modifier) ? "()" : "";
1175
+ changes.push(
1176
+ `removed decorator: @${decoratorName}${decoratorSuffix}`
1177
+ );
1178
+ return false;
1179
+ }
1180
+ return true;
1181
+ });
1182
+ if (filteredModifiers.length === node.modifiers.length) {
1183
+ return node;
1184
+ }
1185
+ return ts6.factory.createClassDeclaration(
1186
+ filteredModifiers,
1187
+ node.name,
1188
+ node.typeParameters,
1189
+ node.heritageClauses,
1190
+ node.members
1191
+ );
1192
+ }
1193
+ extractDecoratorName(decorator) {
1194
+ const expression = decorator.expression;
1195
+ if (ts6.isCallExpression(expression) && ts6.isIdentifier(expression.expression)) {
1196
+ return expression.expression.text;
1197
+ }
1198
+ if (ts6.isIdentifier(expression)) {
1199
+ return expression.text;
1200
+ }
1201
+ return void 0;
1202
+ }
1203
+ isDecoratorCallExpression(decorator) {
1204
+ return ts6.isCallExpression(decorator.expression);
1205
+ }
1206
+ hasExportModifier(node) {
1207
+ return node.modifiers?.some(
1208
+ (m) => m.kind === ts6.SyntaxKind.ExportKeyword
1209
+ ) ?? false;
1210
+ }
1211
+ prependExportModifier(modifiers) {
1212
+ const exportModifier = ts6.factory.createModifier(
1213
+ ts6.SyntaxKind.ExportKeyword
1214
+ );
1215
+ return modifiers ? [exportModifier, ...modifiers] : [exportModifier];
1216
+ }
1217
+ addExportToTypes(content, filePath, typeNames) {
1218
+ const changes = [];
1219
+ const typeNamesSet = new Set(typeNames);
1220
+ const visitorFactory = this.createExportAdditionVisitor(
1221
+ typeNamesSet,
1222
+ changes
1223
+ );
1224
+ const transformedContent = this.transformSourceFile(
1225
+ content,
1226
+ filePath,
1227
+ visitorFactory
1228
+ );
1229
+ return { content: transformedContent, changes };
1230
+ }
1231
+ createExportAdditionVisitor(typeNamesSet, changes) {
1232
+ return (context) => {
1233
+ const visit = (node) => {
1234
+ if (ts6.isTypeAliasDeclaration(node)) {
1235
+ return this.addExportToTypeAlias(
1236
+ node,
1237
+ typeNamesSet,
1238
+ changes
1239
+ );
1240
+ }
1241
+ if (ts6.isInterfaceDeclaration(node)) {
1242
+ return this.addExportToInterface(
1243
+ node,
1244
+ typeNamesSet,
1245
+ changes
1246
+ );
1247
+ }
1248
+ return ts6.visitEachChild(node, visit, context);
1249
+ };
1250
+ return visit;
1251
+ };
1252
+ }
1253
+ addExportToTypeAlias(node, typeNamesSet, changes) {
1254
+ const typeName = node.name.text;
1255
+ if (!typeNamesSet.has(typeName) || this.hasExportModifier(node)) {
1256
+ return node;
1257
+ }
1258
+ changes.push(`added export: type ${typeName}`);
1259
+ return ts6.factory.createTypeAliasDeclaration(
1260
+ this.prependExportModifier(node.modifiers),
1261
+ node.name,
1262
+ node.typeParameters,
1263
+ node.type
1264
+ );
1265
+ }
1266
+ addExportToInterface(node, typeNamesSet, changes) {
1267
+ const typeName = node.name.text;
1268
+ if (!typeNamesSet.has(typeName) || this.hasExportModifier(node)) {
1269
+ return node;
1270
+ }
1271
+ changes.push(`added export: interface ${typeName}`);
1272
+ return ts6.factory.createInterfaceDeclaration(
1273
+ this.prependExportModifier(node.modifiers),
1274
+ node.name,
1275
+ node.typeParameters,
1276
+ node.heritageClauses,
1277
+ node.members
1278
+ );
1279
+ }
1280
+ transformSourceFile(content, filePath, visitorFactory) {
1281
+ const sourceFile = ts6.createSourceFile(
1282
+ filePath,
1283
+ content,
1284
+ ts6.ScriptTarget.Latest,
1285
+ true,
1286
+ ts6.ScriptKind.TS
1287
+ );
1288
+ const transformer = (context) => {
1289
+ return (sf) => {
1290
+ const visitor = visitorFactory(context);
1291
+ return ts6.visitNode(sf, visitor);
1292
+ };
1293
+ };
1294
+ const result = ts6.transform(sourceFile, [transformer]);
1295
+ const transformedSourceFile = result.transformed[0];
1296
+ const printer = ts6.createPrinter({ newLine: ts6.NewLineKind.LineFeed });
1297
+ const transformedContent = printer.printFile(transformedSourceFile);
1298
+ result.dispose();
1299
+ return transformedContent;
1300
+ }
1301
+ extractSymbolsFromEntry(content, filePath, messageTypes, decoratorNames) {
1302
+ const sourceFile = ts6.createSourceFile(
1303
+ filePath,
1304
+ content,
1305
+ ts6.ScriptTarget.Latest,
1306
+ true,
1307
+ ts6.ScriptKind.TS
1308
+ );
1309
+ const decoratorToMessageType = this.buildDecoratorToMessageTypeMap(decoratorNames);
1310
+ const context = {
1311
+ sourceFile,
1312
+ content,
1313
+ messageTypes,
1314
+ decoratorToMessageType
1315
+ };
1316
+ const { targetClassNames, targetClasses } = this.findTargetClasses(context);
1317
+ if (targetClasses.length === 0) {
1318
+ return { content, usedModuleSpecifiers: /* @__PURE__ */ new Set() };
1319
+ }
1320
+ const relatedTypeNames = this.computeRelatedTypeNames(targetClassNames);
1321
+ const usedIdentifiers = this.collectUsedIdentifiers(targetClasses);
1322
+ const localTypeDeclarations = this.collectLocalTypeDeclarations(sourceFile);
1323
+ const includedLocalTypes = this.resolveIncludedLocalTypes(
1324
+ usedIdentifiers,
1325
+ relatedTypeNames,
1326
+ localTypeDeclarations
1327
+ );
1328
+ const extractedSymbols = {
1329
+ targetClassNames,
1330
+ targetClasses,
1331
+ usedIdentifiers,
1332
+ includedLocalTypes,
1333
+ localTypeDeclarations
1334
+ };
1335
+ return this.generateExtractedOutput(context, extractedSymbols);
1336
+ }
1337
+ buildDecoratorToMessageTypeMap(decoratorNames) {
1338
+ const names = { ...DEFAULT_DECORATOR_NAMES, ...decoratorNames };
1339
+ return {
1340
+ [names.event]: "event",
1341
+ [names.command]: "command",
1342
+ [names.query]: "query"
1343
+ };
1344
+ }
1345
+ findTargetClasses(context) {
1346
+ const targetClassNames = /* @__PURE__ */ new Set();
1347
+ const targetClasses = [];
1348
+ const findClasses = (node) => {
1349
+ if (ts6.isClassDeclaration(node) && node.name) {
1350
+ for (const [decoratorName, messageType] of Object.entries(
1351
+ context.decoratorToMessageType
1352
+ )) {
1353
+ if (hasDecorator(node, decoratorName) && context.messageTypes.includes(messageType)) {
1354
+ targetClassNames.add(node.name.text);
1355
+ targetClasses.push(node);
1356
+ break;
1357
+ }
1358
+ }
1359
+ }
1360
+ ts6.forEachChild(node, findClasses);
1361
+ };
1362
+ findClasses(context.sourceFile);
1363
+ return { targetClassNames, targetClasses };
1364
+ }
1365
+ computeRelatedTypeNames(targetClassNames) {
1366
+ const relatedTypeNames = /* @__PURE__ */ new Set();
1367
+ for (const className of targetClassNames) {
1368
+ if (className.endsWith(REQUEST_SUFFIX)) {
1369
+ const baseName = className.slice(0, -REQUEST_SUFFIX.length);
1370
+ relatedTypeNames.add(baseName + "Response");
1371
+ relatedTypeNames.add(baseName + "Payload");
1372
+ } else if (className.endsWith(QUERY_SUFFIX)) {
1373
+ const baseName = className.slice(0, -QUERY_SUFFIX.length);
1374
+ relatedTypeNames.add(baseName + "QueryResult");
1375
+ relatedTypeNames.add(baseName + "Payload");
1376
+ }
1377
+ relatedTypeNames.add(className + "Response");
1378
+ relatedTypeNames.add(className + "Payload");
1379
+ }
1380
+ return relatedTypeNames;
1381
+ }
1382
+ collectUsedIdentifiers(targetClasses) {
1383
+ const usedIdentifiers = /* @__PURE__ */ new Set();
1384
+ const collectIdentifiers = (node) => {
1385
+ if (ts6.isTypeReferenceNode(node) && ts6.isIdentifier(node.typeName)) {
1386
+ usedIdentifiers.add(node.typeName.text);
1387
+ }
1388
+ if (ts6.isExpressionWithTypeArguments(node) && ts6.isIdentifier(node.expression)) {
1389
+ usedIdentifiers.add(node.expression.text);
1390
+ }
1391
+ if (ts6.isIdentifier(node) && node.parent) {
1392
+ const isHeritageOrTypeRef = ts6.isHeritageClause(node.parent.parent) || ts6.isTypeReferenceNode(node.parent);
1393
+ if (isHeritageOrTypeRef) {
1394
+ usedIdentifiers.add(node.text);
1395
+ }
1396
+ }
1397
+ if (ts6.isDecorator(node)) {
1398
+ this.collectDecoratorIdentifier(node, usedIdentifiers);
1399
+ }
1400
+ if (ts6.isCallExpression(node) && ts6.isIdentifier(node.expression)) {
1401
+ usedIdentifiers.add(node.expression.text);
1402
+ }
1403
+ ts6.forEachChild(node, collectIdentifiers);
1404
+ };
1405
+ for (const cls of targetClasses) {
1406
+ collectIdentifiers(cls);
1407
+ }
1408
+ return usedIdentifiers;
1409
+ }
1410
+ collectDecoratorIdentifier(decorator, usedIdentifiers) {
1411
+ const expr = decorator.expression;
1412
+ if (ts6.isIdentifier(expr)) {
1413
+ usedIdentifiers.add(expr.text);
1414
+ } else if (ts6.isCallExpression(expr) && ts6.isIdentifier(expr.expression)) {
1415
+ usedIdentifiers.add(expr.expression.text);
1416
+ }
1417
+ }
1418
+ collectLocalTypeDeclarations(sourceFile) {
1419
+ const localTypeDeclarations = /* @__PURE__ */ new Map();
1420
+ const collectLocalTypes = (node) => {
1421
+ if (ts6.isInterfaceDeclaration(node) && node.name) {
1422
+ this.addToDeclarationMap(
1423
+ localTypeDeclarations,
1424
+ node.name.text,
1425
+ node
1426
+ );
1427
+ }
1428
+ if (ts6.isTypeAliasDeclaration(node) && node.name) {
1429
+ this.addToDeclarationMap(
1430
+ localTypeDeclarations,
1431
+ node.name.text,
1432
+ node
1433
+ );
1434
+ }
1435
+ if (ts6.isVariableStatement(node)) {
1436
+ for (const decl of node.declarationList.declarations) {
1437
+ if (ts6.isIdentifier(decl.name)) {
1438
+ this.addToDeclarationMap(
1439
+ localTypeDeclarations,
1440
+ decl.name.text,
1441
+ node
1442
+ );
1443
+ }
1444
+ }
1445
+ }
1446
+ ts6.forEachChild(node, collectLocalTypes);
1447
+ };
1448
+ collectLocalTypes(sourceFile);
1449
+ return localTypeDeclarations;
1450
+ }
1451
+ addToDeclarationMap(map, name, node) {
1452
+ const existing = map.get(name) ?? [];
1453
+ existing.push(node);
1454
+ map.set(name, existing);
1455
+ }
1456
+ resolveIncludedLocalTypes(usedIdentifiers, relatedTypeNames, localTypeDeclarations) {
1457
+ const includedLocalTypes = /* @__PURE__ */ new Set();
1458
+ const queue = [...usedIdentifiers, ...relatedTypeNames];
1459
+ while (queue.length > 0) {
1460
+ const identifier = queue.shift();
1461
+ if (includedLocalTypes.has(identifier)) continue;
1462
+ const typeNodes = localTypeDeclarations.get(identifier);
1463
+ if (!typeNodes || typeNodes.length === 0) continue;
1464
+ includedLocalTypes.add(identifier);
1465
+ const typeIdentifiers = this.collectTypeIdentifiersFromNodes(typeNodes);
1466
+ for (const id of typeIdentifiers) {
1467
+ if (!includedLocalTypes.has(id)) {
1468
+ queue.push(id);
1469
+ usedIdentifiers.add(id);
1470
+ }
1471
+ }
1472
+ }
1473
+ return includedLocalTypes;
1474
+ }
1475
+ collectTypeIdentifiersFromNodes(nodes) {
1476
+ const typeIdentifiers = /* @__PURE__ */ new Set();
1477
+ const collectFromType = (node) => {
1478
+ if (ts6.isTypeReferenceNode(node) && ts6.isIdentifier(node.typeName)) {
1479
+ typeIdentifiers.add(node.typeName.text);
1480
+ }
1481
+ if (ts6.isExpressionWithTypeArguments(node) && ts6.isIdentifier(node.expression)) {
1482
+ typeIdentifiers.add(node.expression.text);
1483
+ }
1484
+ if (ts6.isComputedPropertyName(node)) {
1485
+ this.collectComputedPropertyIdentifier(node, typeIdentifiers);
1486
+ }
1487
+ ts6.forEachChild(node, collectFromType);
1488
+ };
1489
+ for (const typeNode of nodes) {
1490
+ collectFromType(typeNode);
1491
+ }
1492
+ return typeIdentifiers;
1493
+ }
1494
+ collectComputedPropertyIdentifier(node, typeIdentifiers) {
1495
+ const expr = node.expression;
1496
+ if (ts6.isPropertyAccessExpression(expr) && ts6.isIdentifier(expr.expression)) {
1497
+ typeIdentifiers.add(expr.expression.text);
1498
+ }
1499
+ if (ts6.isIdentifier(expr)) {
1500
+ typeIdentifiers.add(expr.text);
1501
+ }
1502
+ }
1503
+ generateExtractedOutput(context, symbols) {
1504
+ const output = [];
1505
+ const importMap = this.buildImportMap(context.sourceFile);
1506
+ const filteredImports = this.filterImports(
1507
+ symbols.usedIdentifiers,
1508
+ importMap,
1509
+ symbols.includedLocalTypes
1510
+ );
1511
+ this.appendImportStatements(output, filteredImports);
1512
+ this.appendLocalTypeDeclarations(output, context, symbols);
1513
+ this.appendTargetClasses(output, context, symbols.targetClasses);
1514
+ const usedModuleSpecifiers = new Set(filteredImports.keys());
1515
+ return {
1516
+ content: output.join("\n"),
1517
+ usedModuleSpecifiers
1518
+ };
1519
+ }
1520
+ buildImportMap(sourceFile) {
1521
+ const importMap = /* @__PURE__ */ new Map();
1522
+ const collectImports = (node) => {
1523
+ if (ts6.isImportDeclaration(node)) {
1524
+ const moduleSpecifier = node.moduleSpecifier.text;
1525
+ const namedBindings = node.importClause?.namedBindings;
1526
+ const isTypeOnly = node.importClause?.isTypeOnly ?? false;
1527
+ if (namedBindings && ts6.isNamedImports(namedBindings)) {
1528
+ for (const element of namedBindings.elements) {
1529
+ importMap.set(element.name.text, {
1530
+ moduleSpecifier,
1531
+ isTypeOnly
1532
+ });
1533
+ }
1534
+ }
1535
+ }
1536
+ ts6.forEachChild(node, collectImports);
1537
+ };
1538
+ collectImports(sourceFile);
1539
+ return importMap;
1540
+ }
1541
+ filterImports(usedIdentifiers, importMap, includedLocalTypes) {
1542
+ const filteredImports = /* @__PURE__ */ new Map();
1543
+ for (const identifier of usedIdentifiers) {
1544
+ const importInfo = importMap.get(identifier);
1545
+ if (!importInfo || includedLocalTypes.has(identifier)) {
1546
+ continue;
1547
+ }
1548
+ const existing = filteredImports.get(importInfo.moduleSpecifier);
1549
+ if (existing) {
1550
+ existing.identifiers.add(identifier);
1551
+ } else {
1552
+ filteredImports.set(importInfo.moduleSpecifier, {
1553
+ identifiers: /* @__PURE__ */ new Set([identifier]),
1554
+ isTypeOnly: importInfo.isTypeOnly
1555
+ });
1556
+ }
1557
+ }
1558
+ return filteredImports;
1559
+ }
1560
+ appendImportStatements(output, filteredImports) {
1561
+ for (const [moduleSpecifier, info] of filteredImports) {
1562
+ const identifiers = [...info.identifiers].sort().join(", ");
1563
+ const typeOnlyPrefix = info.isTypeOnly ? "type " : "";
1564
+ output.push(
1565
+ `import ${typeOnlyPrefix}{ ${identifiers} } from "${moduleSpecifier}";`
1566
+ );
1567
+ }
1568
+ if (output.length > 0) {
1569
+ output.push("");
1570
+ }
1571
+ }
1572
+ appendLocalTypeDeclarations(output, context, symbols) {
1573
+ const outputNodes = /* @__PURE__ */ new Set();
1574
+ for (const typeName of symbols.includedLocalTypes) {
1575
+ const typeNodes = symbols.localTypeDeclarations.get(typeName);
1576
+ if (!typeNodes) continue;
1577
+ for (const typeNode of typeNodes) {
1578
+ if (outputNodes.has(typeNode)) continue;
1579
+ outputNodes.add(typeNode);
1580
+ this.appendNodeWithExport(output, context, typeNode);
1581
+ }
1582
+ }
1583
+ }
1584
+ appendTargetClasses(output, context, targetClasses) {
1585
+ for (const cls of targetClasses) {
1586
+ this.appendNodeWithExport(output, context, cls);
1587
+ }
1588
+ }
1589
+ appendNodeWithExport(output, context, node) {
1590
+ const nodeText = context.content.substring(node.getStart(context.sourceFile), node.end).trim();
1591
+ const hasExport = this.nodeHasExportKeyword(node);
1592
+ if (hasExport) {
1593
+ output.push(nodeText);
1594
+ } else {
1595
+ output.push("export " + nodeText);
1596
+ }
1597
+ output.push("");
1598
+ }
1599
+ nodeHasExportKeyword(node) {
1600
+ const isExportableDeclaration = ts6.isInterfaceDeclaration(node) || ts6.isTypeAliasDeclaration(node) || ts6.isVariableStatement(node) || ts6.isClassDeclaration(node);
1601
+ if (!isExportableDeclaration) {
1602
+ return false;
1603
+ }
1604
+ const declarationNode = node;
1605
+ return declarationNode.modifiers?.some(
1606
+ (m) => m.kind === ts6.SyntaxKind.ExportKeyword
1607
+ ) ?? false;
1608
+ }
1609
+ };
1610
+ var TYPESCRIPT_EXTENSIONS2 = [".ts", ".tsx", ".d.ts"];
1611
+ var INDEX_FILE2 = "index.ts";
1612
+ var Tsconfig = class _Tsconfig {
1613
+ constructor(paths) {
1614
+ this.paths = paths;
1615
+ }
1616
+ static NONE = new _Tsconfig(/* @__PURE__ */ new Map());
1617
+ static async load(tsconfigPath, fs) {
1618
+ const absolutePath = path3__default.resolve(tsconfigPath);
1619
+ const configDir = path3__default.dirname(absolutePath);
1620
+ const content = await fs.readFile(absolutePath);
1621
+ const { config, error } = ts6__default.parseConfigFileTextToJson(absolutePath, content);
1622
+ if (error) {
1623
+ throw new Error(
1624
+ `Failed to parse tsconfig: ${ts6__default.flattenDiagnosticMessageText(error.messageText, "\n")}`
1625
+ );
1626
+ }
1627
+ const parsed = ts6__default.parseJsonConfigFileContent(config, ts6__default.sys, configDir);
1628
+ const baseUrl = parsed.options.baseUrl ?? configDir;
1629
+ const paths = /* @__PURE__ */ new Map();
1630
+ if (parsed.options.paths) {
1631
+ for (const [alias, targets] of Object.entries(parsed.options.paths)) {
1632
+ const resolvedTargets = targets.map(
1633
+ (target) => path3__default.join(baseUrl, target)
1634
+ );
1635
+ paths.set(alias, resolvedTargets);
1636
+ }
1637
+ }
1638
+ return new _Tsconfig(paths);
1639
+ }
1640
+ /**
1641
+ * Pure string transformation: resolves path alias to potential file paths.
1642
+ * Returns null if no alias matches.
1643
+ */
1644
+ resolvePath(importPath) {
1645
+ for (const [pattern, targets] of this.paths) {
1646
+ const wildcardMatch = this.matchPathPattern(importPath, pattern);
1647
+ if (wildcardMatch === null) {
1648
+ continue;
1649
+ }
1650
+ return targets.map((target) => target.replace("*", wildcardMatch));
1651
+ }
1652
+ return null;
1653
+ }
1654
+ matchPathPattern(moduleSpecifier, pattern) {
1655
+ if (pattern.endsWith("*")) {
1656
+ const prefix = pattern.slice(0, -1);
1657
+ if (moduleSpecifier.startsWith(prefix)) {
1658
+ return moduleSpecifier.slice(prefix.length);
1659
+ }
1660
+ } else if (moduleSpecifier === pattern) {
1661
+ return "";
1662
+ }
1663
+ return null;
1664
+ }
1665
+ };
1666
+ var ContextConfig = class _ContextConfig {
1667
+ fs;
1668
+ tsconfig;
1669
+ name;
1670
+ sourceDir;
1671
+ responseNamingConventions;
1672
+ constructor(name, sourceDir, tsconfig, fs, responseNamingConventions) {
1673
+ this.name = name;
1674
+ this.sourceDir = sourceDir;
1675
+ this.tsconfig = tsconfig;
1676
+ this.fs = fs;
1677
+ this.responseNamingConventions = responseNamingConventions;
1678
+ }
1679
+ /**
1680
+ * Factory method to create ContextConfig with properly loaded tsconfig.
1681
+ */
1682
+ static async create(input, configDir, fs = nodeFileSystem) {
1683
+ if (!input.name) {
1684
+ throw new Error("ContextConfig requires 'name'");
1685
+ }
1686
+ if (!input.path) {
1687
+ throw new Error(`ContextConfig '${input.name}' requires 'path'`);
1688
+ }
1689
+ const basePath = path3__default.resolve(configDir, input.path);
1690
+ const sourceDir = path3__default.resolve(basePath, input.sourceDir ?? "src");
1691
+ const tsconfig = await this.loadTsconfig(basePath, input.tsconfigPath, fs);
1692
+ return new _ContextConfig(
1693
+ input.name,
1694
+ sourceDir,
1695
+ tsconfig,
1696
+ fs,
1697
+ input.responseNamingConventions
1698
+ );
1699
+ }
1700
+ static async loadTsconfig(basePath, inputPath, fs) {
1701
+ const tsconfigPath = path3__default.resolve(basePath, inputPath ?? "tsconfig.json");
1702
+ if (!await fs.exists(tsconfigPath)) {
1703
+ return Tsconfig.NONE;
1704
+ }
1705
+ return Tsconfig.load(tsconfigPath, fs);
1706
+ }
1707
+ /**
1708
+ * Creates a ContextConfig without async loading (for cases where tsconfig is not needed
1709
+ * or already handled externally).
1710
+ */
1711
+ static createSync(name, sourceDir, fs = nodeFileSystem, responseNamingConventions) {
1712
+ return new _ContextConfig(
1713
+ name,
1714
+ sourceDir,
1715
+ Tsconfig.NONE,
1716
+ fs,
1717
+ responseNamingConventions
1718
+ );
1719
+ }
1720
+ /**
1721
+ * Resolves a module specifier (path alias) to actual file path.
1722
+ * Only handles non-relative imports (path aliases).
1723
+ *
1724
+ * @param moduleSpecifier - The import path to resolve (e.g., "@/utils/helper")
1725
+ * @returns Object with resolvedPath (null if external) and isExternal flag
1726
+ */
1727
+ async resolvePath(moduleSpecifier) {
1728
+ const resolvedPaths = this.tsconfig.resolvePath(moduleSpecifier);
1729
+ if (!resolvedPaths) {
1730
+ return { resolvedPath: null, isExternal: true };
1731
+ }
1732
+ for (const resolvedPath of resolvedPaths) {
1733
+ const filePath = await this.tryResolveWithExtensions(resolvedPath);
1734
+ if (filePath && filePath.startsWith(this.sourceDir)) {
1735
+ return { resolvedPath: filePath, isExternal: false };
1736
+ }
1737
+ }
1738
+ return { resolvedPath: null, isExternal: true };
1739
+ }
1740
+ async tryResolveWithExtensions(basePath) {
1741
+ const extensionCandidates = TYPESCRIPT_EXTENSIONS2.map((ext) => basePath + ext);
1742
+ const indexCandidate = path3__default.join(basePath, INDEX_FILE2);
1743
+ const candidates = [...extensionCandidates, indexCandidate];
1744
+ for (const candidate of candidates) {
1745
+ if (await this.fs.exists(candidate)) {
1746
+ return candidate;
1747
+ }
1748
+ }
1749
+ return null;
1750
+ }
1751
+ };
1752
+
1753
+ // src/config-loader.ts
1754
+ var SUPPORTED_GLOB_PARTS_COUNT = 2;
1755
+ async function resolveContextEntries(entries, configDir, fs = nodeFileSystem) {
1756
+ const contexts = [];
1757
+ for (let i = 0; i < entries.length; i++) {
1758
+ const item = entries[i];
1759
+ if (typeof item === "string") {
1760
+ const resolved = await resolveStringEntry(item, configDir, fs);
1761
+ contexts.push(...resolved);
1762
+ } else {
1763
+ const contextConfig = await createObjectContext(item, i, configDir, fs);
1764
+ contexts.push(contextConfig);
1765
+ }
1766
+ }
1767
+ return contexts;
1768
+ }
1769
+ async function resolveStringEntry(contextPath, configDir, fs) {
1770
+ if (contextPath.includes("*")) {
1771
+ return expandGlobPattern(contextPath, configDir, fs);
1772
+ }
1773
+ const basePath = resolve(configDir, contextPath);
1774
+ const name = basename(basePath);
1775
+ return [await ContextConfig.create(
1776
+ { name, path: contextPath },
1777
+ configDir,
1778
+ fs
1779
+ )];
1780
+ }
1781
+ async function expandGlobPattern(pattern, configDir, fs) {
1782
+ const packageDirs = await matchGlobPattern(pattern, configDir, fs);
1783
+ return Promise.all(
1784
+ packageDirs.map((dir) => {
1785
+ const name = basename(dir);
1786
+ const relativePath = relative(configDir, dir);
1787
+ return ContextConfig.create(
1788
+ { name, path: relativePath },
1789
+ configDir,
1790
+ fs
1791
+ );
1792
+ })
1793
+ );
1794
+ }
1795
+ async function createObjectContext(ctx, index, configDir, fs) {
1796
+ if (!ctx.name || typeof ctx.name !== "string") {
1797
+ throw new ConfigLoadError(
1798
+ `Invalid context at index ${index}: missing 'name'`
1799
+ );
1800
+ }
1801
+ if (!ctx.path || typeof ctx.path !== "string") {
1802
+ throw new ConfigLoadError(
1803
+ `Invalid context at index ${index}: missing 'path'`
1804
+ );
1805
+ }
1806
+ return ContextConfig.create(ctx, configDir, fs);
1807
+ }
1808
+ async function matchGlobPattern(pattern, configDir, fs) {
1809
+ const globParts = pattern.split("*");
1810
+ if (globParts.length !== SUPPORTED_GLOB_PARTS_COUNT) {
1811
+ throw new ConfigLoadError(
1812
+ `Invalid glob pattern: "${pattern}". Only single wildcard patterns like "packages/*" are supported.`
1813
+ );
1814
+ }
1815
+ const [prefix, suffix] = globParts;
1816
+ const baseDir = resolve(configDir, prefix);
1817
+ if (!await fs.exists(baseDir)) {
1818
+ return [];
1819
+ }
1820
+ const entries = await fs.readdir(baseDir);
1821
+ const matchedDirs = [];
1822
+ for (const entry of entries) {
1823
+ const fullPath = resolve(baseDir, entry);
1824
+ const stats = await fs.stat(fullPath);
1825
+ if (!stats.isDirectory()) {
1826
+ continue;
1827
+ }
1828
+ if (suffix) {
1829
+ const suffixPath = resolve(fullPath, suffix.replace(/^\//, ""));
1830
+ if (await fs.exists(suffixPath)) {
1831
+ matchedDirs.push(fullPath);
1832
+ }
1833
+ } else {
1834
+ matchedDirs.push(fullPath);
1835
+ }
1836
+ }
1837
+ return matchedDirs.sort();
1838
+ }
1839
+ var ConfigLoader = class {
1840
+ fs;
1841
+ constructor(options = {}) {
1842
+ this.fs = options.fileSystem ?? nodeFileSystem;
1843
+ }
1844
+ async load(configPath) {
1845
+ const absolutePath = resolve(configPath);
1846
+ const config = await this.loadTypeScriptConfig(absolutePath);
1847
+ return this.extractContractsConfig(config, dirname(absolutePath));
1848
+ }
1849
+ async loadTypeScriptConfig(absolutePath) {
1850
+ const source = await this.fs.readFile(absolutePath);
1851
+ const result = ts6__default.transpileModule(source, {
1852
+ compilerOptions: {
1853
+ module: ts6__default.ModuleKind.CommonJS,
1854
+ target: ts6__default.ScriptTarget.ES2020,
1855
+ esModuleInterop: true
1856
+ }
1857
+ });
1858
+ const exports$1 = {};
1859
+ const moduleWrapper = new Function("exports", result.outputText);
1860
+ moduleWrapper(exports$1);
1861
+ return exports$1.default ?? exports$1;
1862
+ }
1863
+ async extractContractsConfig(config, configDir) {
1864
+ const contracts = config.contracts;
1865
+ if (!contracts) {
1866
+ throw new ConfigLoadError("Missing 'contracts' section in config");
1867
+ }
1868
+ if (!contracts.contexts || !Array.isArray(contracts.contexts)) {
1869
+ throw new ConfigLoadError("Missing 'contracts.contexts' in config");
1870
+ }
1871
+ const contexts = await resolveContextEntries(contracts.contexts, configDir, this.fs);
1872
+ if (contexts.length === 0) {
1873
+ throw new ConfigLoadError("No contexts found from 'contexts'");
1874
+ }
1875
+ const decoratorNames = mergeDecoratorNames(contracts.decoratorNames);
1876
+ return {
1877
+ contexts,
1878
+ pathAliasRewrites: contracts.pathAliasRewrites,
1879
+ externalDependencies: contracts.externalDependencies,
1880
+ decoratorNames,
1881
+ responseNamingConventions: contracts.responseNamingConventions,
1882
+ removeDecorators: contracts.removeDecorators ?? true
1883
+ };
1884
+ }
1885
+ };
1886
+
1887
+ // src/registry-generator.ts
1888
+ var DEFAULT_OPTIONS = {
1889
+ messageRegistryImport: "@hexaijs/plugin-contracts-generator/runtime"
1890
+ };
1891
+ function hasMessages(ctx) {
1892
+ return ctx.events.length > 0 || ctx.commands.length > 0 || (ctx.queries?.length ?? 0) > 0;
1893
+ }
1894
+ function getAllMessages(ctx) {
1895
+ return [...ctx.events, ...ctx.commands, ...ctx.queries ?? []];
1896
+ }
1897
+ var RegistryGenerator = class {
1898
+ options;
1899
+ constructor(options = {}) {
1900
+ this.options = { ...DEFAULT_OPTIONS, ...options };
1901
+ }
1902
+ generate(contexts) {
1903
+ const allMessages = contexts.flatMap(
1904
+ (ctx) => getAllMessages(ctx).map((message) => ({
1905
+ message,
1906
+ contextName: ctx.contextName
1907
+ }))
1908
+ );
1909
+ if (allMessages.length === 0) {
1910
+ return this.generateEmptyRegistry();
1911
+ }
1912
+ if (this.options.useNamespace) {
1913
+ return this.generateWithNamespace(contexts, allMessages);
1914
+ }
1915
+ const imports = this.generateImports(contexts);
1916
+ const registrations = this.generateRegistrations(allMessages);
1917
+ return [
1918
+ imports,
1919
+ "",
1920
+ "export const messageRegistry = new MessageRegistry()",
1921
+ registrations
1922
+ ].join("\n");
1923
+ }
1924
+ generateEmptyRegistry() {
1925
+ return [
1926
+ `import { MessageRegistry } from "${this.options.messageRegistryImport}";`,
1927
+ "",
1928
+ "export const messageRegistry = new MessageRegistry();",
1929
+ ""
1930
+ ].join("\n");
1931
+ }
1932
+ generateWithNamespace(contexts, allMessages) {
1933
+ const imports = this.generateNamespaceImports(contexts);
1934
+ const exports$1 = this.generateNamespaceExports(contexts);
1935
+ const registrations = this.generateNamespaceRegistrations(allMessages);
1936
+ return [
1937
+ imports,
1938
+ "",
1939
+ exports$1,
1940
+ "",
1941
+ "export const messageRegistry = new MessageRegistry()",
1942
+ registrations
1943
+ ].join("\n");
1944
+ }
1945
+ getNamespaceInfos(contexts) {
1946
+ return contexts.filter(hasMessages).map((ctx) => ({
1947
+ importPath: ctx.importPath ?? `./${ctx.contextName}`,
1948
+ namespace: this.toNamespace(ctx.contextName)
1949
+ }));
1950
+ }
1951
+ generateNamespaceImports(contexts) {
1952
+ return [
1953
+ `import { MessageRegistry } from "${this.options.messageRegistryImport}";`,
1954
+ ...this.getNamespaceInfos(contexts).map(
1955
+ ({ importPath, namespace }) => `import * as ${namespace} from "${importPath}";`
1956
+ )
1957
+ ].join("\n");
1958
+ }
1959
+ generateNamespaceExports(contexts) {
1960
+ return this.getNamespaceInfos(contexts).map(
1961
+ ({ importPath, namespace }) => `export * as ${namespace} from "${importPath}";`
1962
+ ).join("\n");
1963
+ }
1964
+ generateNamespaceRegistrations(messages) {
1965
+ const lines = messages.map(({ message, contextName }) => {
1966
+ const namespace = this.toNamespace(contextName);
1967
+ return ` .register(${namespace}.${message.name})`;
1968
+ });
1969
+ return lines.join("\n") + ";\n";
1970
+ }
1971
+ toNamespace(contextName) {
1972
+ return contextName.replace(
1973
+ /-([a-z])/g,
1974
+ (_, letter) => letter.toUpperCase()
1975
+ );
1976
+ }
1977
+ generateImports(contexts) {
1978
+ const lines = [
1979
+ `import { MessageRegistry } from "${this.options.messageRegistryImport}";`
1980
+ ];
1981
+ for (const ctx of contexts) {
1982
+ const messageNames = getAllMessages(ctx).map((m) => m.name);
1983
+ if (messageNames.length > 0) {
1984
+ const importPath = ctx.importPath ?? `./${ctx.contextName}`;
1985
+ lines.push(
1986
+ `import { ${messageNames.join(", ")} } from "${importPath}";`
1987
+ );
1988
+ }
1989
+ }
1990
+ return lines.join("\n");
1991
+ }
1992
+ generateRegistrations(messages) {
1993
+ const lines = messages.map(
1994
+ ({ message }) => ` .register(${message.name})`
1995
+ );
1996
+ return lines.join("\n") + ";\n";
1997
+ }
1998
+ };
1999
+ var ReexportGenerator = class {
2000
+ fs;
2001
+ constructor(options = {}) {
2002
+ this.fs = options.fileSystem ?? nodeFileSystem;
2003
+ }
2004
+ /**
2005
+ * Analyzes files to find imports that match pathAliasRewrites
2006
+ * and groups them by rewritten path
2007
+ */
2008
+ async analyze(options) {
2009
+ const { files, pathAliasRewrites } = options;
2010
+ const rewrittenToOriginal = /* @__PURE__ */ new Map();
2011
+ for (const [original, rewritten] of pathAliasRewrites) {
2012
+ rewrittenToOriginal.set(rewritten, original);
2013
+ }
2014
+ const allImports = [];
2015
+ for (const file of files) {
2016
+ const content = await this.fs.readFile(file);
2017
+ const imports = this.extractRewrittenImports(content, rewrittenToOriginal);
2018
+ allImports.push(...imports);
2019
+ }
2020
+ return this.groupImportsByPath(allImports);
2021
+ }
2022
+ /**
2023
+ * Generates re-export files
2024
+ */
2025
+ async generate(options) {
2026
+ const { outputDir, reexportFiles } = options;
2027
+ const generatedFiles = [];
2028
+ for (const reexport of reexportFiles) {
2029
+ const filePath = path3.join(outputDir, reexport.relativePath);
2030
+ const content = this.generateReexportContent(reexport);
2031
+ await this.fs.mkdir(path3.dirname(filePath), { recursive: true });
2032
+ await this.fs.writeFile(filePath, content);
2033
+ generatedFiles.push(filePath);
2034
+ }
2035
+ return generatedFiles;
2036
+ }
2037
+ /**
2038
+ * Extracts imports from source content that match rewritten prefixes
2039
+ */
2040
+ extractRewrittenImports(content, rewrittenToOriginal) {
2041
+ const sourceFile = ts6.createSourceFile(
2042
+ "temp.ts",
2043
+ content,
2044
+ ts6.ScriptTarget.Latest,
2045
+ true
2046
+ );
2047
+ const imports = [];
2048
+ ts6.forEachChild(sourceFile, (node) => {
2049
+ if (!ts6.isImportDeclaration(node) || !node.importClause) {
2050
+ return;
2051
+ }
2052
+ const moduleSpecifier = node.moduleSpecifier.text;
2053
+ for (const [rewrittenPrefix, originalPrefix] of rewrittenToOriginal) {
2054
+ if (moduleSpecifier.startsWith(rewrittenPrefix)) {
2055
+ const symbols = this.extractSymbolNames(node.importClause);
2056
+ if (symbols.length > 0) {
2057
+ const suffix = moduleSpecifier.slice(rewrittenPrefix.length);
2058
+ const originalPath = originalPrefix + suffix;
2059
+ imports.push({
2060
+ rewrittenPath: moduleSpecifier,
2061
+ originalPath,
2062
+ symbols,
2063
+ isTypeOnly: node.importClause.isTypeOnly ?? false
2064
+ });
2065
+ }
2066
+ break;
2067
+ }
2068
+ }
2069
+ });
2070
+ return imports;
2071
+ }
2072
+ /**
2073
+ * Extracts symbol names from import clause
2074
+ */
2075
+ extractSymbolNames(importClause) {
2076
+ const names = [];
2077
+ if (importClause.name) {
2078
+ names.push(importClause.name.text);
2079
+ }
2080
+ if (importClause.namedBindings && ts6.isNamedImports(importClause.namedBindings)) {
2081
+ for (const element of importClause.namedBindings.elements) {
2082
+ const originalName = element.propertyName?.text ?? element.name.text;
2083
+ names.push(originalName);
2084
+ }
2085
+ }
2086
+ return names;
2087
+ }
2088
+ /**
2089
+ * Groups imports by rewritten path and merges symbols
2090
+ */
2091
+ groupImportsByPath(imports) {
2092
+ const grouped = /* @__PURE__ */ new Map();
2093
+ for (const imp of imports) {
2094
+ const existing = grouped.get(imp.rewrittenPath);
2095
+ if (existing) {
2096
+ for (const symbol of imp.symbols) {
2097
+ existing.symbols.add(symbol);
2098
+ }
2099
+ if (!imp.isTypeOnly) {
2100
+ existing.hasValueImport = true;
2101
+ }
2102
+ } else {
2103
+ grouped.set(imp.rewrittenPath, {
2104
+ originalPath: imp.originalPath,
2105
+ symbols: new Set(imp.symbols),
2106
+ hasValueImport: !imp.isTypeOnly
2107
+ });
2108
+ }
2109
+ }
2110
+ const result = [];
2111
+ for (const [rewrittenPath, data] of grouped) {
2112
+ const relativePath = this.rewrittenPathToRelativePath(rewrittenPath);
2113
+ result.push({
2114
+ relativePath,
2115
+ originalModule: data.originalPath,
2116
+ symbols: Array.from(data.symbols).sort(),
2117
+ isTypeOnly: !data.hasValueImport
2118
+ });
2119
+ }
2120
+ return result.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
2121
+ }
2122
+ /**
2123
+ * Converts a rewritten package path to a relative file path
2124
+ * e.g., "@libera/contracts/common/request" with prefix "@libera/contracts"
2125
+ * -> "common/request.ts"
2126
+ */
2127
+ rewrittenPathToRelativePath(rewrittenPath) {
2128
+ const parts = rewrittenPath.split("/");
2129
+ let subpathStart;
2130
+ if (parts[0].startsWith("@")) {
2131
+ subpathStart = 2;
2132
+ } else {
2133
+ subpathStart = 1;
2134
+ }
2135
+ const subpath = parts.slice(subpathStart).join("/");
2136
+ if (!subpath) {
2137
+ return "index.ts";
2138
+ }
2139
+ return subpath + ".ts";
2140
+ }
2141
+ /**
2142
+ * Generates the content for a re-export file
2143
+ */
2144
+ generateReexportContent(reexport) {
2145
+ const exportKeyword = reexport.isTypeOnly ? "export type" : "export";
2146
+ const symbols = reexport.symbols.join(", ");
2147
+ return `${exportKeyword} { ${symbols} } from "${reexport.originalModule}";
2148
+ `;
2149
+ }
2150
+ };
2151
+
2152
+ // src/logger.ts
2153
+ var LOG_LEVEL_PRIORITY = {
2154
+ debug: 0,
2155
+ info: 1,
2156
+ warn: 2,
2157
+ error: 3
2158
+ };
2159
+ var ConsoleLogger = class {
2160
+ level;
2161
+ constructor(options = {}) {
2162
+ this.level = options.level ?? "info";
2163
+ }
2164
+ debug(message) {
2165
+ if (this.shouldLog("debug")) {
2166
+ console.debug(message);
2167
+ }
2168
+ }
2169
+ info(message) {
2170
+ if (this.shouldLog("info")) {
2171
+ console.info(message);
2172
+ }
2173
+ }
2174
+ warn(message) {
2175
+ if (this.shouldLog("warn")) {
2176
+ console.warn(message);
2177
+ }
2178
+ }
2179
+ error(message) {
2180
+ if (this.shouldLog("error")) {
2181
+ console.error(message);
2182
+ }
2183
+ }
2184
+ shouldLog(level) {
2185
+ return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[this.level];
2186
+ }
2187
+ };
2188
+ var NoopLogger = class {
2189
+ debug() {
2190
+ }
2191
+ info() {
2192
+ }
2193
+ warn() {
2194
+ }
2195
+ error() {
2196
+ }
2197
+ };
2198
+ var noopLogger = new NoopLogger();
2199
+ var DEFAULT_EXCLUDE_DEPENDENCIES = [
2200
+ "**/*.test.ts",
2201
+ "**/*.spec.ts",
2202
+ "**/*.eh.ts",
2203
+ "**/db.ts",
2204
+ "**/infra/**"
2205
+ ];
2206
+ var ContractsPipeline = class _ContractsPipeline {
2207
+ constructor(deps, messageTypes) {
2208
+ this.deps = deps;
2209
+ this.messageTypes = messageTypes;
2210
+ }
2211
+ messageTypes;
2212
+ static create(options) {
2213
+ const fileSystem = options.fileSystem ?? nodeFileSystem;
2214
+ const logger = options.logger ?? noopLogger;
2215
+ const excludeDependencies = options.excludeDependencies ?? DEFAULT_EXCLUDE_DEPENDENCIES;
2216
+ const scanner = new Scanner({ fileSystem, messageTypes: options.messageTypes });
2217
+ const parser = new Parser({
2218
+ responseNamingConventions: options.responseNamingConventions ?? options.contextConfig.responseNamingConventions,
2219
+ messageTypes: options.messageTypes
2220
+ });
2221
+ const fileGraphResolver = FileGraphResolver.create({
2222
+ contextConfig: options.contextConfig,
2223
+ fileSystem,
2224
+ excludeDependencies
2225
+ });
2226
+ const fileCopier = new FileCopier({ fileSystem });
2227
+ return new _ContractsPipeline(
2228
+ {
2229
+ scanner,
2230
+ parser,
2231
+ fileGraphResolver,
2232
+ fileCopier,
83
2233
  fileSystem,
84
- logger,
2234
+ logger
2235
+ },
2236
+ options.messageTypes
2237
+ );
2238
+ }
2239
+ static fromDependencies(deps) {
2240
+ return new _ContractsPipeline(deps);
2241
+ }
2242
+ async execute(options) {
2243
+ const { contextName, sourceDir, outputDir, pathAliasRewrites, removeDecorators } = options;
2244
+ const contextOutputDir = join(outputDir, contextName);
2245
+ this.deps.logger.info(`Processing context: ${contextName}`);
2246
+ this.deps.logger.debug(` Source: ${sourceDir}`);
2247
+ this.deps.logger.debug(` Output: ${contextOutputDir}`);
2248
+ const decoratedFiles = await this.scan(sourceDir);
2249
+ const messages = await this.parse(decoratedFiles, sourceDir);
2250
+ const fileGraph = await this.resolve(decoratedFiles, sourceDir);
2251
+ const responseTypesToExport = this.collectResponseTypesToExport(messages);
2252
+ const copiedFiles = await this.copy(fileGraph, sourceDir, contextOutputDir, pathAliasRewrites, responseTypesToExport, removeDecorators, this.messageTypes);
2253
+ await this.exportBarrel(copiedFiles, contextOutputDir);
2254
+ this.deps.logger.info(`Completed context: ${contextName} (${messages.events.length} events, ${messages.commands.length} commands, ${messages.queries.length} queries, ${copiedFiles.length} files)`);
2255
+ return {
2256
+ events: messages.events,
2257
+ commands: messages.commands,
2258
+ queries: messages.queries,
2259
+ copiedFiles
2260
+ };
2261
+ }
2262
+ collectResponseTypesToExport(messages) {
2263
+ const result = /* @__PURE__ */ new Map();
2264
+ const allMessages = [...messages.commands, ...messages.queries];
2265
+ for (const message of allMessages) {
2266
+ if (message.resultType?.kind !== "reference") continue;
2267
+ const typeName = message.resultType.name;
2268
+ const sourceFile = message.sourceFile.absolutePath;
2269
+ const typeDef = messages.typeDefinitions.find(
2270
+ (t) => t.name === typeName && t.sourceFile.absolutePath === sourceFile && !t.exported
2271
+ );
2272
+ if (typeDef) {
2273
+ const existing = result.get(sourceFile) ?? [];
2274
+ if (!existing.includes(typeName)) {
2275
+ existing.push(typeName);
2276
+ result.set(sourceFile, existing);
2277
+ }
2278
+ }
2279
+ }
2280
+ if (result.size > 0) {
2281
+ this.deps.logger.debug(`Found ${result.size} file(s) with unexported response types`);
2282
+ }
2283
+ return result;
2284
+ }
2285
+ async scan(sourceDir) {
2286
+ this.deps.logger.debug(`Scanning for decorated files in ${sourceDir}`);
2287
+ const files = await this.deps.scanner.scan(sourceDir);
2288
+ this.deps.logger.debug(`Found ${files.length} decorated file(s)`);
2289
+ return files;
2290
+ }
2291
+ async parse(files, sourceRoot) {
2292
+ this.deps.logger.debug(`Parsing ${files.length} file(s)`);
2293
+ const events = [];
2294
+ const commands = [];
2295
+ const queries = [];
2296
+ const typeDefinitions = [];
2297
+ for (const file of files) {
2298
+ const content = await this.deps.fileSystem.readFile(file);
2299
+ const sourceFileInfo = {
2300
+ absolutePath: file,
2301
+ relativePath: relative(sourceRoot, file)
2302
+ };
2303
+ const result = this.deps.parser.parse(content, sourceFileInfo);
2304
+ events.push(...result.events);
2305
+ commands.push(...result.commands);
2306
+ queries.push(...result.queries);
2307
+ typeDefinitions.push(...result.typeDefinitions);
2308
+ }
2309
+ this.deps.logger.debug(`Parsed ${events.length} event(s), ${commands.length} command(s), ${queries.length} query(s), ${typeDefinitions.length} type(s)`);
2310
+ return { events, commands, queries, typeDefinitions };
2311
+ }
2312
+ async resolve(entryPoints, sourceRoot) {
2313
+ this.deps.logger.debug(`Resolving dependencies for ${entryPoints.length} entry point(s)`);
2314
+ const graph = await this.deps.fileGraphResolver.buildGraph(entryPoints, sourceRoot);
2315
+ this.deps.logger.debug(`Resolved ${graph.nodes.size} file(s) in dependency graph`);
2316
+ return graph;
2317
+ }
2318
+ async copy(fileGraph, sourceRoot, outputDir, pathAliasRewrites, responseTypesToExport, removeDecorators, messageTypes) {
2319
+ this.deps.logger.debug(`Copying files to ${outputDir}`);
2320
+ await this.deps.fileSystem.mkdir(outputDir, { recursive: true });
2321
+ const result = await this.deps.fileCopier.copyFiles({
2322
+ sourceRoot,
2323
+ outputDir,
2324
+ fileGraph,
2325
+ pathAliasRewrites,
2326
+ responseTypesToExport,
2327
+ removeDecorators,
2328
+ messageTypes
85
2329
  });
86
- return pipeline.execute({
87
- contextName,
88
- sourceDir,
89
- outputDir,
90
- pathAliasRewrites,
91
- removeDecorators,
2330
+ this.deps.logger.debug(`Copied ${result.copiedFiles.length} file(s)`);
2331
+ return result.copiedFiles;
2332
+ }
2333
+ async exportBarrel(copiedFiles, outputDir) {
2334
+ this.deps.logger.debug(`Generating barrel export at ${outputDir}/index.ts`);
2335
+ const indexContent = this.deps.fileCopier.generateBarrelExport(copiedFiles, outputDir);
2336
+ await this.deps.fileSystem.writeFile(join(outputDir, "index.ts"), indexContent);
2337
+ }
2338
+ };
2339
+ var DEFAULT_CONFIG_PATH = "application.config.ts";
2340
+ var EXIT_CODE_ERROR = 1;
2341
+ var VALID_MESSAGE_TYPES = ["event", "command", "query"];
2342
+ var CLI_OPTIONS = {
2343
+ config: { short: "-c", long: "--config"},
2344
+ outputDir: { short: "-o", long: "--output-dir"},
2345
+ messageTypes: { short: "-m", long: "--message-types"},
2346
+ generateMessageRegistry: { short: null, long: "--generate-message-registry"},
2347
+ help: { short: "-h", long: "--help"}
2348
+ };
2349
+ function parseMessageTypes(value) {
2350
+ const types = value.split(",").map((type) => type.trim().toLowerCase());
2351
+ const invalidTypes = types.filter((type) => !VALID_MESSAGE_TYPES.includes(type));
2352
+ if (invalidTypes.length > 0) {
2353
+ throw new Error(
2354
+ `Invalid message type(s): ${invalidTypes.join(", ")}. Valid types are: ${VALID_MESSAGE_TYPES.join(", ")}`
2355
+ );
2356
+ }
2357
+ return types;
2358
+ }
2359
+ function extractOptionValue(args, currentIndex, optionName) {
2360
+ const currentArg = args[currentIndex];
2361
+ const equalsIndex = currentArg.indexOf("=");
2362
+ if (equalsIndex !== -1) {
2363
+ return {
2364
+ value: currentArg.slice(equalsIndex + 1),
2365
+ nextIndex: currentIndex
2366
+ };
2367
+ }
2368
+ const nextValue = args[currentIndex + 1];
2369
+ if (!nextValue) {
2370
+ throw new Error(`Missing value for ${optionName} option`);
2371
+ }
2372
+ return {
2373
+ value: nextValue,
2374
+ nextIndex: currentIndex + 1
2375
+ };
2376
+ }
2377
+ function matchesOption(arg, option) {
2378
+ const matchesShortOrLong = arg === option.short || arg === option.long;
2379
+ const matchesLongWithValue = option.long !== null && arg.startsWith(`${option.long}=`);
2380
+ return matchesShortOrLong || matchesLongWithValue;
2381
+ }
2382
+ function parseArgs(args) {
2383
+ const options = {
2384
+ config: DEFAULT_CONFIG_PATH
2385
+ };
2386
+ for (let i = 0; i < args.length; i++) {
2387
+ const arg = args[i];
2388
+ if (matchesOption(arg, CLI_OPTIONS.config)) {
2389
+ const { value, nextIndex } = extractOptionValue(args, i, "--config");
2390
+ options.config = value;
2391
+ i = nextIndex;
2392
+ } else if (matchesOption(arg, CLI_OPTIONS.outputDir)) {
2393
+ const { value, nextIndex } = extractOptionValue(args, i, "--output-dir");
2394
+ options.outputDir = value;
2395
+ i = nextIndex;
2396
+ } else if (matchesOption(arg, CLI_OPTIONS.messageTypes)) {
2397
+ const { value, nextIndex } = extractOptionValue(args, i, "--message-types");
2398
+ options.messageTypes = parseMessageTypes(value);
2399
+ i = nextIndex;
2400
+ } else if (matchesOption(arg, CLI_OPTIONS.generateMessageRegistry)) {
2401
+ options.generateMessageRegistry = true;
2402
+ } else if (matchesOption(arg, CLI_OPTIONS.help)) {
2403
+ printHelp();
2404
+ process.exit(0);
2405
+ }
2406
+ }
2407
+ if (!options.outputDir) {
2408
+ throw new Error("Missing required option: --output-dir");
2409
+ }
2410
+ return options;
2411
+ }
2412
+ function printHelp() {
2413
+ console.log(`
2414
+ contracts-generator - Extract domain events and commands from TypeScript source
2415
+
2416
+ Usage:
2417
+ contracts-generator --output-dir <path> [options]
2418
+
2419
+ Required:
2420
+ -o, --output-dir <path> Output directory for generated contracts
2421
+
2422
+ Options:
2423
+ -c, --config <path> Path to config file (default: ${DEFAULT_CONFIG_PATH})
2424
+ -m, --message-types <types> Filter message types to extract (comma-separated)
2425
+ Valid types: ${VALID_MESSAGE_TYPES.join(", ")}
2426
+ Default: all types
2427
+ --generate-message-registry Generate message registry index.ts file
2428
+ Default: not generated
2429
+ -h, --help Show this help message
2430
+
2431
+ Config file format:
2432
+ export default {
2433
+ contracts: {
2434
+ contexts: [
2435
+ "packages/*", // glob: auto-discover all contexts
2436
+ "packages/auth", // string: name inferred from directory
2437
+ {
2438
+ name: "lecture",
2439
+ path: "packages/lecture", // required: base directory
2440
+ sourceDir: "src", // optional, default: "src"
2441
+ },
2442
+ ],
2443
+ pathAliasRewrites: {
2444
+ "@/": "@libera/",
2445
+ },
2446
+ },
2447
+ };
2448
+
2449
+ Examples:
2450
+ # Extract all message types
2451
+ contracts-generator --output-dir packages/contracts/src
2452
+
2453
+ # Extract only commands and queries
2454
+ contracts-generator -o packages/contracts/requests -m command,query
2455
+
2456
+ # Extract only events
2457
+ contracts-generator -o packages/contracts/events --message-types event
2458
+
2459
+ # Generate with message registry (index.ts)
2460
+ contracts-generator -o packages/contracts/src --generate-message-registry
2461
+
2462
+ # Use with custom config
2463
+ contracts-generator -c app.config.ts -o packages/contracts/src
2464
+ `);
2465
+ }
2466
+ function calculateSummaryTotals(results) {
2467
+ return results.reduce(
2468
+ (totals, contextResult) => ({
2469
+ events: totals.events + contextResult.result.events.length,
2470
+ commands: totals.commands + contextResult.result.commands.length,
2471
+ files: totals.files + contextResult.result.copiedFiles.length
2472
+ }),
2473
+ { events: 0, commands: 0, files: 0 }
2474
+ );
2475
+ }
2476
+ function countTotalMessages(totals) {
2477
+ return totals.events + totals.commands;
2478
+ }
2479
+ function logSummary(logger, totals) {
2480
+ logger.info("\n--- Summary ---");
2481
+ logger.info(`Total events: ${totals.events}`);
2482
+ logger.info(`Total commands: ${totals.commands}`);
2483
+ logger.info(`Total files copied: ${totals.files}`);
2484
+ }
2485
+ async function run(args) {
2486
+ const options = parseArgs(args);
2487
+ const configPath = resolve(options.config);
2488
+ const configDir = dirname(configPath);
2489
+ const outputDir = resolve(configDir, options.outputDir);
2490
+ const logger = new ConsoleLogger({ level: "info" });
2491
+ logger.info(`Loading config from: ${configPath}`);
2492
+ const configLoader = new ConfigLoader();
2493
+ const config = await configLoader.load(configPath);
2494
+ logger.info(`Found ${config.contexts.length} context(s) to process`);
2495
+ logger.info(`Output directory: ${outputDir}`);
2496
+ if (options.messageTypes) {
2497
+ logger.info(`Message types filter: ${options.messageTypes.join(", ")}`);
2498
+ }
2499
+ const pathAliasRewrites = config.pathAliasRewrites ? new Map(Object.entries(config.pathAliasRewrites)) : void 0;
2500
+ const results = [];
2501
+ for (const contextConfig of config.contexts) {
2502
+ const pipeline = ContractsPipeline.create({
2503
+ contextConfig,
2504
+ messageTypes: options.messageTypes,
2505
+ logger
2506
+ });
2507
+ const result = await pipeline.execute({
2508
+ contextName: contextConfig.name,
2509
+ sourceDir: contextConfig.sourceDir,
2510
+ outputDir,
2511
+ pathAliasRewrites,
2512
+ removeDecorators: config.removeDecorators
2513
+ });
2514
+ results.push({ name: contextConfig.name, result, outputDir });
2515
+ }
2516
+ const totals = calculateSummaryTotals(results);
2517
+ logSummary(logger, totals);
2518
+ if (options.generateMessageRegistry) {
2519
+ await generateRegistry(outputDir, results, totals, logger);
2520
+ }
2521
+ if (config.pathAliasRewrites) {
2522
+ await generateReexports(config, outputDir, results, logger);
2523
+ }
2524
+ }
2525
+ async function toContractsConfig(pluginConfig) {
2526
+ const contexts = await resolveContextEntries(
2527
+ pluginConfig.contexts,
2528
+ process.cwd(),
2529
+ nodeFileSystem
2530
+ );
2531
+ return {
2532
+ contexts,
2533
+ pathAliasRewrites: pluginConfig.pathAliasRewrites,
2534
+ externalDependencies: pluginConfig.externalDependencies,
2535
+ decoratorNames: mergeDecoratorNames(pluginConfig.decoratorNames),
2536
+ responseNamingConventions: pluginConfig.responseNamingConventions,
2537
+ removeDecorators: pluginConfig.removeDecorators ?? true
2538
+ };
2539
+ }
2540
+ async function runWithConfig(options, pluginConfig) {
2541
+ const outputDir = resolve(options.outputDir);
2542
+ const logger = new ConsoleLogger({ level: "info" });
2543
+ const config = await toContractsConfig(pluginConfig);
2544
+ logger.info(`Found ${config.contexts.length} context(s) to process`);
2545
+ logger.info(`Output directory: ${outputDir}`);
2546
+ if (options.messageTypes) {
2547
+ logger.info(`Message types filter: ${options.messageTypes.join(", ")}`);
2548
+ }
2549
+ const pathAliasRewrites = config.pathAliasRewrites ? new Map(Object.entries(config.pathAliasRewrites)) : void 0;
2550
+ const results = [];
2551
+ for (const contextConfig of config.contexts) {
2552
+ const pipeline = ContractsPipeline.create({
2553
+ contextConfig,
2554
+ messageTypes: options.messageTypes,
2555
+ logger
92
2556
  });
2557
+ const result = await pipeline.execute({
2558
+ contextName: contextConfig.name,
2559
+ sourceDir: contextConfig.sourceDir,
2560
+ outputDir,
2561
+ pathAliasRewrites,
2562
+ removeDecorators: config.removeDecorators
2563
+ });
2564
+ results.push({ name: contextConfig.name, result, outputDir });
2565
+ }
2566
+ const totals = calculateSummaryTotals(results);
2567
+ logSummary(logger, totals);
2568
+ if (options.generateMessageRegistry) {
2569
+ await generateRegistry(outputDir, results, totals, logger);
2570
+ }
2571
+ if (config.pathAliasRewrites) {
2572
+ await generateReexports(config, outputDir, results, logger);
2573
+ }
2574
+ }
2575
+ async function generateRegistry(outputDir, results, totals, logger) {
2576
+ const contextMessages = results.map((contextResult) => {
2577
+ const contextOutputDir = join(contextResult.outputDir, contextResult.name);
2578
+ const importPath = "./" + relative(outputDir, contextOutputDir).replace(/\\/g, "/");
2579
+ return {
2580
+ contextName: contextResult.name,
2581
+ events: contextResult.result.events,
2582
+ commands: contextResult.result.commands,
2583
+ queries: contextResult.result.queries,
2584
+ importPath
2585
+ };
2586
+ });
2587
+ const generator = new RegistryGenerator({ useNamespace: true });
2588
+ const registryContent = generator.generate(contextMessages);
2589
+ await nodeFileSystem.mkdir(outputDir, { recursive: true });
2590
+ const indexPath = join(outputDir, "index.ts");
2591
+ await nodeFileSystem.writeFile(indexPath, registryContent);
2592
+ logger.info(` Generated index.ts with ${countTotalMessages(totals)} message(s)`);
2593
+ }
2594
+ async function generateReexports(config, outputDir, results, logger) {
2595
+ logger.info("\n--- Generating re-exports for pathAliasRewrites ---");
2596
+ const pathAliasRewrites = new Map(Object.entries(config.pathAliasRewrites));
2597
+ const allCopiedFiles = results.flatMap((contextResult) => contextResult.result.copiedFiles);
2598
+ const generator = new ReexportGenerator({ fileSystem: nodeFileSystem });
2599
+ const reexportFiles = await generator.analyze({
2600
+ files: allCopiedFiles,
2601
+ pathAliasRewrites
2602
+ });
2603
+ if (reexportFiles.length === 0) {
2604
+ logger.info(" No re-export files needed");
2605
+ return;
2606
+ }
2607
+ const generatedFiles = await generator.generate({
2608
+ outputDir,
2609
+ reexportFiles
2610
+ });
2611
+ logger.info(` Generated ${generatedFiles.length} re-export file(s):`);
2612
+ for (const file of generatedFiles) {
2613
+ logger.info(` - ${relative(outputDir, file)}`);
2614
+ }
2615
+ }
2616
+ async function main() {
2617
+ try {
2618
+ await run(process.argv.slice(2));
2619
+ } catch (error) {
2620
+ if (error instanceof Error) {
2621
+ console.error(`Error: ${error.message}`);
2622
+ } else {
2623
+ console.error("Unknown error occurred");
2624
+ }
2625
+ process.exit(EXIT_CODE_ERROR);
2626
+ }
2627
+ }
2628
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
2629
+ main();
2630
+ }
2631
+
2632
+ // src/hexai-plugin.ts
2633
+ var VALID_MESSAGE_TYPES2 = ["event", "command", "query"];
2634
+ function parseMessageTypes2(value) {
2635
+ const types = value.split(",").map((type) => type.trim().toLowerCase());
2636
+ const invalidTypes = types.filter(
2637
+ (type) => !VALID_MESSAGE_TYPES2.includes(type)
2638
+ );
2639
+ if (invalidTypes.length > 0) {
2640
+ throw new Error(
2641
+ `Invalid message type(s): ${invalidTypes.join(", ")}. Valid types are: ${VALID_MESSAGE_TYPES2.join(", ")}`
2642
+ );
2643
+ }
2644
+ return types;
2645
+ }
2646
+ var cliPlugin = {
2647
+ name: "generate-contracts",
2648
+ description: "Extract domain events, commands, and queries from bounded contexts",
2649
+ options: [
2650
+ {
2651
+ flags: "-o, --output-dir <path>",
2652
+ description: "Output directory for generated contracts",
2653
+ required: true
2654
+ },
2655
+ {
2656
+ flags: "-m, --message-types <types>",
2657
+ description: "Filter message types (comma-separated: event,command,query)"
2658
+ },
2659
+ {
2660
+ flags: "--generate-message-registry",
2661
+ description: "Generate message registry index.ts file"
2662
+ }
2663
+ ],
2664
+ run: async (args, config) => {
2665
+ const options = {
2666
+ outputDir: String(args.outputDir),
2667
+ messageTypes: args.messageTypes ? parseMessageTypes2(String(args.messageTypes)) : void 0,
2668
+ generateMessageRegistry: args.generateMessageRegistry === true
2669
+ };
2670
+ await runWithConfig(options, config);
2671
+ }
2672
+ };
2673
+
2674
+ // src/index.ts
2675
+ async function processContext(options) {
2676
+ const {
2677
+ contextName,
2678
+ path: contextPath,
2679
+ sourceDir,
2680
+ outputDir,
2681
+ pathAliasRewrites,
2682
+ tsconfigPath,
2683
+ responseNamingConventions,
2684
+ removeDecorators,
2685
+ messageTypes,
2686
+ fileSystem = nodeFileSystem,
2687
+ logger = noopLogger
2688
+ } = options;
2689
+ const contextConfig = await ContextConfig.create(
2690
+ {
2691
+ name: contextName,
2692
+ path: contextPath,
2693
+ sourceDir,
2694
+ tsconfigPath,
2695
+ responseNamingConventions
2696
+ },
2697
+ process.cwd(),
2698
+ fileSystem
2699
+ );
2700
+ return ContractsPipeline.create({
2701
+ contextConfig,
2702
+ responseNamingConventions,
2703
+ messageTypes,
2704
+ fileSystem,
2705
+ logger
2706
+ }).execute({
2707
+ contextName,
2708
+ sourceDir: contextConfig.sourceDir,
2709
+ outputDir,
2710
+ pathAliasRewrites,
2711
+ removeDecorators
2712
+ });
93
2713
  }
94
- // Hexai CLI plugin integration
95
- var hexai_plugin_1 = require("./hexai-plugin");
96
- Object.defineProperty(exports, "cliPlugin", { enumerable: true, get: function () { return hexai_plugin_1.cliPlugin; } });
2714
+
2715
+ export { ConfigLoadError, ConfigLoader, ConfigurationError, ConsoleLogger, ContextConfig, ContractsPipeline, FileCopier, FileGraphResolver, FileNotFoundError, FileReadError, FileSystemError, FileWriteError, JsonParseError, MessageParserError, ModuleResolutionError, ParseError, Parser, ReexportGenerator, RegistryGenerator, ResolutionError, Scanner, cliPlugin, isArrayType, isCommand, isDomainEvent, isFunctionType, isIntersectionType, isLiteralType, isObjectType, isPrimitiveType, isReferenceType, isTupleType, isUnionType, nodeFileSystem, noopLogger, processContext, resolveContextEntries };
2716
+ //# sourceMappingURL=index.js.map
97
2717
  //# sourceMappingURL=index.js.map