micro-generate 1.3.10 → 1.3.12

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 (80) hide show
  1. package/README.md +83 -9
  2. package/dist/cli/generators/add-mongo-generator.d.ts +4 -0
  3. package/dist/cli/generators/add-mongo-generator.js +212 -0
  4. package/dist/cli/generators/add-mongo-generator.js.map +1 -0
  5. package/dist/cli/generators/add-mysql-generator.d.ts +4 -0
  6. package/dist/cli/generators/add-mysql-generator.js +221 -0
  7. package/dist/cli/generators/add-mysql-generator.js.map +1 -0
  8. package/dist/cli/generators/add-redis-generator.d.ts +1 -0
  9. package/dist/cli/generators/add-redis-generator.js +100 -0
  10. package/dist/cli/generators/add-redis-generator.js.map +1 -0
  11. package/dist/cli/generators/feature-generator.js +26 -338
  12. package/dist/cli/generators/feature-generator.js.map +1 -1
  13. package/dist/cli/generators/init-generator.js +31 -649
  14. package/dist/cli/generators/init-generator.js.map +1 -1
  15. package/dist/cli/generators/model-generator.d.ts +6 -0
  16. package/dist/cli/generators/model-generator.js +50 -0
  17. package/dist/cli/generators/model-generator.js.map +1 -0
  18. package/dist/cli/index.js +71 -0
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/cli/templates/feature/mongoModel.template.d.ts +6 -0
  21. package/dist/cli/templates/feature/mongoModel.template.js +57 -0
  22. package/dist/cli/templates/feature/mongoModel.template.js.map +1 -0
  23. package/dist/cli/templates/feature/mysqlModel.template.d.ts +6 -0
  24. package/dist/cli/templates/feature/mysqlModel.template.js +62 -0
  25. package/dist/cli/templates/feature/mysqlModel.template.js.map +1 -0
  26. package/dist/cli/templates/feature/resolvers.template.d.ts +1 -0
  27. package/dist/cli/templates/feature/resolvers.template.js +21 -0
  28. package/dist/cli/templates/feature/resolvers.template.js.map +1 -0
  29. package/dist/cli/templates/feature/service.template.d.ts +11 -0
  30. package/dist/cli/templates/feature/service.template.js +68 -0
  31. package/dist/cli/templates/feature/service.template.js.map +1 -0
  32. package/dist/cli/templates/feature/typeDefs.template.d.ts +1 -0
  33. package/dist/cli/templates/feature/typeDefs.template.js +24 -0
  34. package/dist/cli/templates/feature/typeDefs.template.js.map +1 -0
  35. package/dist/cli/templates/project/api.template.d.ts +1 -0
  36. package/dist/cli/templates/project/api.template.js +40 -0
  37. package/dist/cli/templates/project/api.template.js.map +1 -0
  38. package/dist/cli/templates/project/config.template.d.ts +1 -0
  39. package/dist/cli/templates/project/config.template.js +12 -0
  40. package/dist/cli/templates/project/config.template.js.map +1 -0
  41. package/dist/cli/templates/project/featuresIndex.template.d.ts +1 -0
  42. package/dist/cli/templates/project/featuresIndex.template.js +4 -0
  43. package/dist/cli/templates/project/featuresIndex.template.js.map +1 -0
  44. package/dist/cli/templates/project/index.template.d.ts +8 -0
  45. package/dist/cli/templates/project/index.template.js +104 -0
  46. package/dist/cli/templates/project/index.template.js.map +1 -0
  47. package/dist/cli/templates/project/mongo.template.d.ts +1 -0
  48. package/dist/cli/templates/project/mongo.template.js +51 -0
  49. package/dist/cli/templates/project/mongo.template.js.map +1 -0
  50. package/dist/cli/templates/project/mongoClient.template.d.ts +1 -0
  51. package/dist/cli/templates/project/mongoClient.template.js +21 -0
  52. package/dist/cli/templates/project/mongoClient.template.js.map +1 -0
  53. package/dist/cli/templates/project/mysqlClient.template.d.ts +1 -0
  54. package/dist/cli/templates/project/mysqlClient.template.js +18 -0
  55. package/dist/cli/templates/project/mysqlClient.template.js.map +1 -0
  56. package/dist/cli/templates/project/redis.template.d.ts +1 -0
  57. package/dist/cli/templates/project/redis.template.js +255 -0
  58. package/dist/cli/templates/project/redis.template.js.map +1 -0
  59. package/dist/cli/templates/project/sanitize.template.d.ts +1 -0
  60. package/dist/cli/templates/project/sanitize.template.js +11 -0
  61. package/dist/cli/templates/project/sanitize.template.js.map +1 -0
  62. package/dist/cli/templates/project/skill.template.d.ts +1 -0
  63. package/dist/cli/templates/project/skill.template.js +147 -0
  64. package/dist/cli/templates/project/skill.template.js.map +1 -0
  65. package/dist/cli/templates/project/tsconfig.template.d.ts +1 -0
  66. package/dist/cli/templates/project/tsconfig.template.js +17 -0
  67. package/dist/cli/templates/project/tsconfig.template.js.map +1 -0
  68. package/dist/core/MicroserviceServer.d.ts +4 -0
  69. package/dist/core/MicroserviceServer.js +13 -0
  70. package/dist/core/MicroserviceServer.js.map +1 -1
  71. package/dist/database/mysql.d.ts +23 -0
  72. package/dist/database/mysql.js +68 -0
  73. package/dist/database/mysql.js.map +1 -0
  74. package/dist/database/redis.d.ts +1 -1
  75. package/dist/database/redis.js +6 -8
  76. package/dist/database/redis.js.map +1 -1
  77. package/dist/index.d.ts +1 -0
  78. package/dist/index.js +1 -0
  79. package/dist/index.js.map +1 -1
  80. package/package.json +5 -1
@@ -0,0 +1,104 @@
1
+ export const getIndexTemplate = (options) => {
2
+ const { useMongo, useRedis, useMySQL, dbNames, mysqlDbNames } = options;
3
+ let indexImports = `import { MicroserviceServer } from 'micro-generate';
4
+ import { typeDefs, resolvers } from './features/index.js';
5
+ import * as dotenv from 'dotenv';\n`;
6
+ if (useMongo) {
7
+ indexImports += `import { setMongoDBConnector } from './database/mongo/client.js';\n`;
8
+ }
9
+ if (useRedis) {
10
+ indexImports += `import { createRedisConnector } from './database/redis.js';\n`;
11
+ }
12
+ if (useMySQL) {
13
+ indexImports += `import { MySQLConnector, setMySQLConnector } from './database/mysql/client.js';\n`;
14
+ }
15
+ const indexConfig = [];
16
+ indexConfig.push(` port: parseInt(process.env.PORT || '4000')`);
17
+ if (useMongo) {
18
+ indexConfig.push(` mongoUri: process.env.MONGO_URI || ''`);
19
+ }
20
+ let redisConnectorInit = '';
21
+ if (useRedis) {
22
+ redisConnectorInit = `
23
+ const redisConnector = createRedisConnector({
24
+ host: process.env.REDIS_HOST || 'localhost',
25
+ port: parseInt(process.env.REDIS_PORT || '6379'),
26
+ password: process.env.REDIS_PASSWORD,
27
+ db: parseInt(process.env.REDIS_DB || '0')
28
+ });
29
+ `;
30
+ indexConfig.push(` redisConnector: redisConnector`);
31
+ }
32
+ if (useMongo && dbNames.length > 0) {
33
+ const secondaryConfig = dbNames.map(name => {
34
+ const envVar = `MONGODB_URI_${name.toUpperCase().replace(/-/g, '_')}`;
35
+ return `${name}: process.env.${envVar} || ''`;
36
+ }).join(',\n ');
37
+ indexConfig.push(` secondaryConnections: {\n ${secondaryConfig}\n }`);
38
+ }
39
+ if (useMySQL) {
40
+ indexConfig.push(` mysqlConnector: mysqlConnector`);
41
+ }
42
+ indexConfig.push(` playground: process.env.GRAPHQL_PLAYGROUND === 'true'`);
43
+ indexConfig.push(` introspection: process.env.GRAPHQL_INTROSPECTION === 'true'`);
44
+ indexConfig.push(` graphqlPath: process.env.GRAPHQL_PATH || '/graphql'`);
45
+ let mysqlInit = '';
46
+ if (useMySQL) {
47
+ mysqlInit = `
48
+ // MySQL Configuration
49
+ const mysqlConfigs = [
50
+ {
51
+ name: 'default',
52
+ database: process.env.MYSQL_DB || '',
53
+ user: process.env.MYSQL_USER || '',
54
+ password: process.env.MYSQL_PASSWORD,
55
+ host: process.env.MYSQL_HOST || 'localhost',
56
+ port: parseInt(process.env.MYSQL_PORT || '3306'),
57
+ },
58
+ ${mysqlDbNames.map(name => {
59
+ const upper = name.toUpperCase().replace(/-/g, '_');
60
+ return `{
61
+ name: '${name}',
62
+ database: process.env.MYSQL_DB_${upper} || '',
63
+ user: process.env.MYSQL_USER_${upper} || '',
64
+ password: process.env.MYSQL_PASSWORD_${upper},
65
+ host: process.env.MYSQL_HOST_${upper} || 'localhost',
66
+ port: parseInt(process.env.MYSQL_PORT_${upper} || '3306'),
67
+ }`;
68
+ }).join(',\n ')}
69
+ ];
70
+
71
+ const mysqlConnector = new MySQLConnector(mysqlConfigs);
72
+ setMySQLConnector(mysqlConnector);
73
+ `;
74
+ }
75
+ return `${indexImports}
76
+ dotenv.config();
77
+ ${redisConnectorInit}
78
+ ${mysqlInit}
79
+ const server = new MicroserviceServer({
80
+ ${indexConfig.join(',\n')},
81
+ typeDefs,
82
+ resolvers,
83
+ });
84
+
85
+ server.start().then(async () => {
86
+ ${useMongo ? `if (server.getMongoDBConnector()) {
87
+ setMongoDBConnector(server.getMongoDBConnector()! as any);
88
+ }` : ''}
89
+
90
+ console.log('Server initialized');
91
+ });
92
+
93
+ const gracefulShutdown = async () => {
94
+ console.log('Starting graceful shutdown...');
95
+ await server.stop();
96
+ console.log('Graceful shutdown complete.');
97
+ process.exit(0);
98
+ };
99
+
100
+ process.on('SIGINT', gracefulShutdown);
101
+ process.on('SIGTERM', gracefulShutdown);
102
+ `;
103
+ };
104
+ //# sourceMappingURL=index.template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.template.js","sourceRoot":"","sources":["../../../../src/cli/templates/project/index.template.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAA6B,EAAU,EAAE;IACtE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IAExE,IAAI,YAAY,GAAG;;oCAEa,CAAC;IAEjC,IAAI,QAAQ,EAAE,CAAC;QACX,YAAY,IAAI,qEAAqE,CAAC;IAC1F,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACX,YAAY,IAAI,+DAA+D,CAAC;IACpF,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACX,YAAY,IAAI,mFAAmF,CAAC;IACxG,CAAC;IAED,MAAM,WAAW,GAAG,EAAE,CAAC;IACvB,WAAW,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;IAEjE,IAAI,QAAQ,EAAE,CAAC;QACX,WAAW,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,kBAAkB,GAAG,EAAE,CAAC;IAC5B,IAAI,QAAQ,EAAE,CAAC;QACX,kBAAkB,GAAG;;;;;;;CAO5B,CAAC;QACM,WAAW,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACvC,MAAM,MAAM,GAAG,eAAe,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;YACtE,OAAO,GAAG,IAAI,iBAAiB,MAAM,QAAQ,CAAC;QAClD,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnB,WAAW,CAAC,IAAI,CAAC,kCAAkC,eAAe,OAAO,CAAC,CAAC;IAC/E,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACX,WAAW,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IACzD,CAAC;IAED,WAAW,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;IAC5E,WAAW,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;IAClF,WAAW,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;IAE1E,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,QAAQ,EAAE,CAAC;QACX,SAAS,GAAG;;;;;;;;;;;MAWd,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACtB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACpD,OAAO;iBACE,IAAI;yCACoB,KAAK;uCACP,KAAK;+CACG,KAAK;uCACb,KAAK;gDACI,KAAK;MAC/C,CAAC;QACH,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;;;;;CAKrB,CAAC;IACE,CAAC;IAED,OAAO,GAAG,YAAY;;EAExB,kBAAkB;EAClB,SAAS;;EAET,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;;;;;;MAMnB,QAAQ,CAAC,CAAC,CAAC;;MAEX,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;CAcV,CAAC;AACF,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare const mongoTemplate = "import mongoose from 'mongoose';\n\nexport interface MongoDBOptions {\n uri: string;\n options?: mongoose.ConnectOptions;\n secondaryConnections?: Record<string, string>;\n}\n\nexport class MongoDBConnector {\n private uri: string;\n private options: mongoose.ConnectOptions;\n private secondaryConnectionsConfig: Record<string, string> = {};\n private isConnected = false;\n private secondaryConnections: Map<string, mongoose.Connection> = new Map();\n\n constructor(config: MongoDBOptions) {\n this.uri = config.uri;\n this.options = config.options || {};\n this.secondaryConnectionsConfig = config.secondaryConnections || {};\n }\n\n async connect(): Promise<void> {\n if (this.isConnected) return;\n try {\n await mongoose.connect(this.uri, this.options);\n this.isConnected = true;\n console.log('\u2705 Primary MongoDB connected');\n\n const secondaryPromises = Object.entries(this.secondaryConnectionsConfig).map(async ([name, uri]) => {\n const conn = mongoose.createConnection(uri, this.options);\n await conn.asPromise();\n this.secondaryConnections.set(name, conn);\n console.log(`\u2705 Secondary MongoDB '${name}' connected`);\n });\n await Promise.all(secondaryPromises);\n } catch (error) {\n console.error('MongoDB connection error:', error);\n throw error;\n }\n }\n\n getSecondaryConnection(name: string): mongoose.Connection | undefined {\n return this.secondaryConnections.get(name);\n }\n}\n\nexport function createMongoDBConnector(uri: string, secondaryConnections?: Record<string, string>): MongoDBConnector {\n return new MongoDBConnector({ uri, secondaryConnections });\n}\n";
@@ -0,0 +1,51 @@
1
+ export const mongoTemplate = `import mongoose from 'mongoose';
2
+
3
+ export interface MongoDBOptions {
4
+ uri: string;
5
+ options?: mongoose.ConnectOptions;
6
+ secondaryConnections?: Record<string, string>;
7
+ }
8
+
9
+ export class MongoDBConnector {
10
+ private uri: string;
11
+ private options: mongoose.ConnectOptions;
12
+ private secondaryConnectionsConfig: Record<string, string> = {};
13
+ private isConnected = false;
14
+ private secondaryConnections: Map<string, mongoose.Connection> = new Map();
15
+
16
+ constructor(config: MongoDBOptions) {
17
+ this.uri = config.uri;
18
+ this.options = config.options || {};
19
+ this.secondaryConnectionsConfig = config.secondaryConnections || {};
20
+ }
21
+
22
+ async connect(): Promise<void> {
23
+ if (this.isConnected) return;
24
+ try {
25
+ await mongoose.connect(this.uri, this.options);
26
+ this.isConnected = true;
27
+ console.log('✅ Primary MongoDB connected');
28
+
29
+ const secondaryPromises = Object.entries(this.secondaryConnectionsConfig).map(async ([name, uri]) => {
30
+ const conn = mongoose.createConnection(uri, this.options);
31
+ await conn.asPromise();
32
+ this.secondaryConnections.set(name, conn);
33
+ console.log(\`✅ Secondary MongoDB '\${name}' connected\`);
34
+ });
35
+ await Promise.all(secondaryPromises);
36
+ } catch (error) {
37
+ console.error('MongoDB connection error:', error);
38
+ throw error;
39
+ }
40
+ }
41
+
42
+ getSecondaryConnection(name: string): mongoose.Connection | undefined {
43
+ return this.secondaryConnections.get(name);
44
+ }
45
+ }
46
+
47
+ export function createMongoDBConnector(uri: string, secondaryConnections?: Record<string, string>): MongoDBConnector {
48
+ return new MongoDBConnector({ uri, secondaryConnections });
49
+ }
50
+ `;
51
+ //# sourceMappingURL=mongo.template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongo.template.js","sourceRoot":"","sources":["../../../../src/cli/templates/project/mongo.template.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiD5B,CAAC"}
@@ -0,0 +1 @@
1
+ export declare const mongoClientTemplate = "import { MongoDBConnector } from '../mongo.js';\nimport mongoose from 'mongoose';\n\nlet mongoConnector: MongoDBConnector | null = null;\n\nexport function setMongoDBConnector(connector: MongoDBConnector): void {\n mongoConnector = connector;\n}\n\nexport function getMongoDBConnector(): MongoDBConnector {\n if (!mongoConnector) {\n throw new Error('MongoDB connector not initialized. Make sure to connect in main.ts');\n }\n return mongoConnector;\n}\n\nexport function getSecondaryConnection(name: string): mongoose.Connection | undefined {\n return getMongoDBConnector().getSecondaryConnection(name);\n}\n";
@@ -0,0 +1,21 @@
1
+ export const mongoClientTemplate = `import { MongoDBConnector } from '../mongo.js';
2
+ import mongoose from 'mongoose';
3
+
4
+ let mongoConnector: MongoDBConnector | null = null;
5
+
6
+ export function setMongoDBConnector(connector: MongoDBConnector): void {
7
+ mongoConnector = connector;
8
+ }
9
+
10
+ export function getMongoDBConnector(): MongoDBConnector {
11
+ if (!mongoConnector) {
12
+ throw new Error('MongoDB connector not initialized. Make sure to connect in main.ts');
13
+ }
14
+ return mongoConnector;
15
+ }
16
+
17
+ export function getSecondaryConnection(name: string): mongoose.Connection | undefined {
18
+ return getMongoDBConnector().getSecondaryConnection(name);
19
+ }
20
+ `;
21
+ //# sourceMappingURL=mongoClient.template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongoClient.template.js","sourceRoot":"","sources":["../../../../src/cli/templates/project/mongoClient.template.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;CAmBlC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare const mysqlClientTemplate = "import { Sequelize } from 'sequelize';\nimport { MySQLConnector, setMySQLConnector as coreSetMySQLConnector, getMySQLConnector as coreGetMySQLConnector, getSecondaryMySQLConnection as coreGetSecondaryMySQLConnection } from 'micro-generate';\n\nexport { MySQLConnector };\n\nexport const setMySQLConnector = coreSetMySQLConnector;\nexport const getMySQLConnector = coreGetMySQLConnector;\nexport const getSecondaryMySQLConnection = coreGetSecondaryMySQLConnection;\n\nexport default () => {\n try {\n return getMySQLConnector().getConnection('default');\n } catch (e) {\n return null;\n }\n};\n";
@@ -0,0 +1,18 @@
1
+ export const mysqlClientTemplate = `import { Sequelize } from 'sequelize';
2
+ import { MySQLConnector, setMySQLConnector as coreSetMySQLConnector, getMySQLConnector as coreGetMySQLConnector, getSecondaryMySQLConnection as coreGetSecondaryMySQLConnection } from 'micro-generate';
3
+
4
+ export { MySQLConnector };
5
+
6
+ export const setMySQLConnector = coreSetMySQLConnector;
7
+ export const getMySQLConnector = coreGetMySQLConnector;
8
+ export const getSecondaryMySQLConnection = coreGetSecondaryMySQLConnection;
9
+
10
+ export default () => {
11
+ try {
12
+ return getMySQLConnector().getConnection('default');
13
+ } catch (e) {
14
+ return null;
15
+ }
16
+ };
17
+ `;
18
+ //# sourceMappingURL=mysqlClient.template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysqlClient.template.js","sourceRoot":"","sources":["../../../../src/cli/templates/project/mysqlClient.template.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;CAgBlC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare const redisTemplate = "import { Redis, RedisOptions } from 'ioredis';\nimport { logger } from 'micro-generate';\n\nexport class RedisConnector {\n private defaultClient: Redis | null = null;\n private clients: Map<number, Redis> = new Map();\n private options: RedisOptions = {};\n private defaultDb: number = 0;\n private host?: string;\n\n constructor(optionsOrUrl: RedisOptions | string) {\n if (typeof optionsOrUrl === 'string') {\n this.options = {\n retryStrategy: (times: number) => Math.min(times * 50, 2000)\n };\n this.host = undefined; \n (this as any).url = optionsOrUrl; \n } else {\n this.options = {\n retryStrategy: (times: number) => Math.min(times * 50, 2000),\n ...optionsOrUrl\n };\n this.defaultDb = optionsOrUrl.db || 0;\n this.host = optionsOrUrl.host;\n }\n }\n\n async connect(): Promise<void> {\n if (this.defaultClient) {\n logger.info('Redis default client already connected');\n return;\n }\n try {\n this.defaultClient = this.createClient(this.defaultDb);\n this.clients.set(this.defaultDb, this.defaultClient);\n await this.defaultClient.connect();\n } catch (error) {\n logger.error(`Failed to connect to Redis: ${error}`);\n throw error;\n }\n }\n\n private createClient(db: number): Redis {\n let client: Redis;\n const url = (this as any).url;\n\n if (url) {\n client = new Redis(url, { ...this.options, db, lazyConnect: true });\n } else {\n client = new Redis({ ...this.options, db, lazyConnect: true });\n }\n\n client.on('connect', () => logger.info(`\uD83D\uDD34 Redis connected to db ${db}`));\n client.on('error', (err) => logger.error(`Redis error on db ${db}: ${err}`));\n return client;\n }\n\n async disconnect(): Promise<void> {\n const disconnectPromises: Promise<void>[] = [];\n for (const [db, client] of this.clients.entries()) {\n disconnectPromises.push(client.quit().then(() => {}).catch(() => client.disconnect()));\n }\n await Promise.all(disconnectPromises);\n this.clients.clear();\n this.defaultClient = null;\n }\n\n getClient(): Redis {\n if (!this.defaultClient) throw new Error('Redis not connected');\n return this.defaultClient;\n }\n\n async getConnection(db: number): Promise<Redis> {\n if (db === this.defaultDb) {\n if (!this.defaultClient) await this.connect();\n return this.defaultClient!;\n }\n if (this.clients.has(db)) {\n const client = this.clients.get(db)!;\n if (client.status === 'wait') await client.connect();\n return client;\n }\n const newClient = this.createClient(db);\n this.clients.set(db, newClient);\n await newClient.connect();\n return newClient;\n }\n}\n\nexport let redisConnectorInstance: RedisConnector | null = null;\n\nexport const setRedisConnectorInstance = (connector: RedisConnector) => {\n redisConnectorInstance = connector;\n};\n\nexport const getRedisConnector = (): RedisConnector => {\n if (!redisConnectorInstance) {\n throw new Error('Redis connector not initialized.');\n }\n return redisConnectorInstance;\n};\n\nexport const createRedisRepository = (getDb: () => (Redis | Promise<Redis>), keyPrefix: string) => {\n const promiseFunct = (err: any, response: any, resolve: (value: any) => void, reject: (reason?: any) => void) => {\n if (err) reject(err);\n else resolve(response);\n };\n\n const resolveDb = async (): Promise<Redis> => {\n const dbOrPromise = getDb();\n return dbOrPromise instanceof Promise ? await dbOrPromise : dbOrPromise;\n };\n\n return {\n find: async (_id: string) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.get(_id, (err, response) => promiseFunct(err, response, resolve, reject));\n });\n },\n saveTTL: async (_id: string, _data: any, ttl: number) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.set(_id, _data, (err, response) => {\n if (err) reject(err);\n else {\n db.expire(_id, ttl);\n resolve(response);\n }\n });\n });\n },\n findKeys: async (_id: string) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.keys(_id, (err, response) => {\n if (response && response.length > 0) {\n promiseFunct(err, response, resolve, reject);\n } else {\n promiseFunct(null, ['No genero datos'], resolve, reject);\n }\n });\n });\n },\n\n findAll: async () => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.keys(keyPrefix + '*', (err, response) => {\n if (response && response.length > 0) {\n db.mget(response, (err, response2) => {\n promiseFunct(err, response2, resolve, reject);\n });\n } else {\n promiseFunct(null, ['No genero datos'], resolve, reject);\n }\n });\n });\n },\n\n save: async (_id: string, _data: any) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.set(_id, _data, (err, response) => {\n promiseFunct(err, response, resolve, reject);\n });\n });\n },\n\n addTTL: async (_id: string, ttl: number) => {\n const db = await resolveDb();\n return db.expire(_id, ttl);\n },\n\n getTTL: async (_id: string) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.ttl(_id, (err, response) => {\n promiseFunct(err, response, resolve, reject);\n });\n });\n },\n\n incr: async (_id: string) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.incr(_id, (err, response) => {\n promiseFunct(err, response, resolve, reject);\n });\n });\n },\n\n sadd: async (_id: string, _data: any) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.sadd(_id, _data, (err, response) => {\n promiseFunct(err, response, resolve, reject);\n });\n });\n },\n\n sismember: async (_id: string, _data: any) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.sismember(_id, _data, (err, response) => {\n promiseFunct(err, response, resolve, reject);\n });\n });\n },\n\n sisremove: async (_id: string, _data: any) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.srem(_id, _data, (err, response) => {\n promiseFunct(err, response, resolve, reject);\n });\n });\n },\n\n smembers: async (_id: string) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.smembers(_id, (err, response) => {\n promiseFunct(err, response, resolve, reject);\n });\n });\n },\n\n sdiff: async (keys: string[]) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.sdiff(keys, (err, response) => {\n promiseFunct(err, response, resolve, reject);\n });\n });\n },\n\n clear: async (key: string) => {\n const db = await resolveDb();\n return new Promise((resolve, reject) => {\n db.del(key, (err, response) => {\n promiseFunct(err, response, resolve, reject);\n });\n });\n }\n };\n};\n\nexport function createRedisConnector(urlOrOptions: string | RedisOptions): RedisConnector {\n const connector = new RedisConnector(urlOrOptions);\n setRedisConnectorInstance(connector);\n return connector;\n}\n";
@@ -0,0 +1,255 @@
1
+ export const redisTemplate = `import { Redis, RedisOptions } from 'ioredis';
2
+ import { logger } from 'micro-generate';
3
+
4
+ export class RedisConnector {
5
+ private defaultClient: Redis | null = null;
6
+ private clients: Map<number, Redis> = new Map();
7
+ private options: RedisOptions = {};
8
+ private defaultDb: number = 0;
9
+ private host?: string;
10
+
11
+ constructor(optionsOrUrl: RedisOptions | string) {
12
+ if (typeof optionsOrUrl === 'string') {
13
+ this.options = {
14
+ retryStrategy: (times: number) => Math.min(times * 50, 2000)
15
+ };
16
+ this.host = undefined;
17
+ (this as any).url = optionsOrUrl;
18
+ } else {
19
+ this.options = {
20
+ retryStrategy: (times: number) => Math.min(times * 50, 2000),
21
+ ...optionsOrUrl
22
+ };
23
+ this.defaultDb = optionsOrUrl.db || 0;
24
+ this.host = optionsOrUrl.host;
25
+ }
26
+ }
27
+
28
+ async connect(): Promise<void> {
29
+ if (this.defaultClient) {
30
+ logger.info('Redis default client already connected');
31
+ return;
32
+ }
33
+ try {
34
+ this.defaultClient = this.createClient(this.defaultDb);
35
+ this.clients.set(this.defaultDb, this.defaultClient);
36
+ await this.defaultClient.connect();
37
+ } catch (error) {
38
+ logger.error(\`Failed to connect to Redis: \${error}\`);
39
+ throw error;
40
+ }
41
+ }
42
+
43
+ private createClient(db: number): Redis {
44
+ let client: Redis;
45
+ const url = (this as any).url;
46
+
47
+ if (url) {
48
+ client = new Redis(url, { ...this.options, db, lazyConnect: true });
49
+ } else {
50
+ client = new Redis({ ...this.options, db, lazyConnect: true });
51
+ }
52
+
53
+ client.on('connect', () => logger.info(\`🔴 Redis connected to db \${db}\`));
54
+ client.on('error', (err) => logger.error(\`Redis error on db \${db}: \${err}\`));
55
+ return client;
56
+ }
57
+
58
+ async disconnect(): Promise<void> {
59
+ const disconnectPromises: Promise<void>[] = [];
60
+ for (const [db, client] of this.clients.entries()) {
61
+ disconnectPromises.push(client.quit().then(() => {}).catch(() => client.disconnect()));
62
+ }
63
+ await Promise.all(disconnectPromises);
64
+ this.clients.clear();
65
+ this.defaultClient = null;
66
+ }
67
+
68
+ getClient(): Redis {
69
+ if (!this.defaultClient) throw new Error('Redis not connected');
70
+ return this.defaultClient;
71
+ }
72
+
73
+ async getConnection(db: number): Promise<Redis> {
74
+ if (db === this.defaultDb) {
75
+ if (!this.defaultClient) await this.connect();
76
+ return this.defaultClient!;
77
+ }
78
+ if (this.clients.has(db)) {
79
+ const client = this.clients.get(db)!;
80
+ if (client.status === 'wait') await client.connect();
81
+ return client;
82
+ }
83
+ const newClient = this.createClient(db);
84
+ this.clients.set(db, newClient);
85
+ await newClient.connect();
86
+ return newClient;
87
+ }
88
+ }
89
+
90
+ export let redisConnectorInstance: RedisConnector | null = null;
91
+
92
+ export const setRedisConnectorInstance = (connector: RedisConnector) => {
93
+ redisConnectorInstance = connector;
94
+ };
95
+
96
+ export const getRedisConnector = (): RedisConnector => {
97
+ if (!redisConnectorInstance) {
98
+ throw new Error('Redis connector not initialized.');
99
+ }
100
+ return redisConnectorInstance;
101
+ };
102
+
103
+ export const createRedisRepository = (getDb: () => (Redis | Promise<Redis>), keyPrefix: string) => {
104
+ const promiseFunct = (err: any, response: any, resolve: (value: any) => void, reject: (reason?: any) => void) => {
105
+ if (err) reject(err);
106
+ else resolve(response);
107
+ };
108
+
109
+ const resolveDb = async (): Promise<Redis> => {
110
+ const dbOrPromise = getDb();
111
+ return dbOrPromise instanceof Promise ? await dbOrPromise : dbOrPromise;
112
+ };
113
+
114
+ return {
115
+ find: async (_id: string) => {
116
+ const db = await resolveDb();
117
+ return new Promise((resolve, reject) => {
118
+ db.get(_id, (err, response) => promiseFunct(err, response, resolve, reject));
119
+ });
120
+ },
121
+ saveTTL: async (_id: string, _data: any, ttl: number) => {
122
+ const db = await resolveDb();
123
+ return new Promise((resolve, reject) => {
124
+ db.set(_id, _data, (err, response) => {
125
+ if (err) reject(err);
126
+ else {
127
+ db.expire(_id, ttl);
128
+ resolve(response);
129
+ }
130
+ });
131
+ });
132
+ },
133
+ findKeys: async (_id: string) => {
134
+ const db = await resolveDb();
135
+ return new Promise((resolve, reject) => {
136
+ db.keys(_id, (err, response) => {
137
+ if (response && response.length > 0) {
138
+ promiseFunct(err, response, resolve, reject);
139
+ } else {
140
+ promiseFunct(null, ['No genero datos'], resolve, reject);
141
+ }
142
+ });
143
+ });
144
+ },
145
+
146
+ findAll: async () => {
147
+ const db = await resolveDb();
148
+ return new Promise((resolve, reject) => {
149
+ db.keys(keyPrefix + '*', (err, response) => {
150
+ if (response && response.length > 0) {
151
+ db.mget(response, (err, response2) => {
152
+ promiseFunct(err, response2, resolve, reject);
153
+ });
154
+ } else {
155
+ promiseFunct(null, ['No genero datos'], resolve, reject);
156
+ }
157
+ });
158
+ });
159
+ },
160
+
161
+ save: async (_id: string, _data: any) => {
162
+ const db = await resolveDb();
163
+ return new Promise((resolve, reject) => {
164
+ db.set(_id, _data, (err, response) => {
165
+ promiseFunct(err, response, resolve, reject);
166
+ });
167
+ });
168
+ },
169
+
170
+ addTTL: async (_id: string, ttl: number) => {
171
+ const db = await resolveDb();
172
+ return db.expire(_id, ttl);
173
+ },
174
+
175
+ getTTL: async (_id: string) => {
176
+ const db = await resolveDb();
177
+ return new Promise((resolve, reject) => {
178
+ db.ttl(_id, (err, response) => {
179
+ promiseFunct(err, response, resolve, reject);
180
+ });
181
+ });
182
+ },
183
+
184
+ incr: async (_id: string) => {
185
+ const db = await resolveDb();
186
+ return new Promise((resolve, reject) => {
187
+ db.incr(_id, (err, response) => {
188
+ promiseFunct(err, response, resolve, reject);
189
+ });
190
+ });
191
+ },
192
+
193
+ sadd: async (_id: string, _data: any) => {
194
+ const db = await resolveDb();
195
+ return new Promise((resolve, reject) => {
196
+ db.sadd(_id, _data, (err, response) => {
197
+ promiseFunct(err, response, resolve, reject);
198
+ });
199
+ });
200
+ },
201
+
202
+ sismember: async (_id: string, _data: any) => {
203
+ const db = await resolveDb();
204
+ return new Promise((resolve, reject) => {
205
+ db.sismember(_id, _data, (err, response) => {
206
+ promiseFunct(err, response, resolve, reject);
207
+ });
208
+ });
209
+ },
210
+
211
+ sisremove: async (_id: string, _data: any) => {
212
+ const db = await resolveDb();
213
+ return new Promise((resolve, reject) => {
214
+ db.srem(_id, _data, (err, response) => {
215
+ promiseFunct(err, response, resolve, reject);
216
+ });
217
+ });
218
+ },
219
+
220
+ smembers: async (_id: string) => {
221
+ const db = await resolveDb();
222
+ return new Promise((resolve, reject) => {
223
+ db.smembers(_id, (err, response) => {
224
+ promiseFunct(err, response, resolve, reject);
225
+ });
226
+ });
227
+ },
228
+
229
+ sdiff: async (keys: string[]) => {
230
+ const db = await resolveDb();
231
+ return new Promise((resolve, reject) => {
232
+ db.sdiff(keys, (err, response) => {
233
+ promiseFunct(err, response, resolve, reject);
234
+ });
235
+ });
236
+ },
237
+
238
+ clear: async (key: string) => {
239
+ const db = await resolveDb();
240
+ return new Promise((resolve, reject) => {
241
+ db.del(key, (err, response) => {
242
+ promiseFunct(err, response, resolve, reject);
243
+ });
244
+ });
245
+ }
246
+ };
247
+ };
248
+
249
+ export function createRedisConnector(urlOrOptions: string | RedisOptions): RedisConnector {
250
+ const connector = new RedisConnector(urlOrOptions);
251
+ setRedisConnectorInstance(connector);
252
+ return connector;
253
+ }
254
+ `;
255
+ //# sourceMappingURL=redis.template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.template.js","sourceRoot":"","sources":["../../../../src/cli/templates/project/redis.template.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6P5B,CAAC"}
@@ -0,0 +1 @@
1
+ export declare const sanitizeTemplate = "import DOMPurify from 'dompurify';\nimport { JSDOM } from 'jsdom';\n\nconst window = new JSDOM('').window;\nconst purify = DOMPurify(window as any);\n\nexport function sanitize(dirty: string): string {\n return purify.sanitize(dirty, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });\n}\n";
@@ -0,0 +1,11 @@
1
+ export const sanitizeTemplate = `import DOMPurify from 'dompurify';
2
+ import { JSDOM } from 'jsdom';
3
+
4
+ const window = new JSDOM('').window;
5
+ const purify = DOMPurify(window as any);
6
+
7
+ export function sanitize(dirty: string): string {
8
+ return purify.sanitize(dirty, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });
9
+ }
10
+ `;
11
+ //# sourceMappingURL=sanitize.template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.template.js","sourceRoot":"","sources":["../../../../src/cli/templates/project/sanitize.template.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;CAS/B,CAAC"}
@@ -0,0 +1 @@
1
+ export declare const skillTemplate = "---\nname: micro-generate-helper\ndescription: Use the micro-generate CLI non-interactively to generate features, queries, mutations, standalone models, and add database/cache configurations to existing projects.\n---\n\n# Micro-Generate Skill\n\nThis skill allows you to programmatically invoke the \\`micro-generate\\` CLI tools to create features, GraphQL operations, standalone models, and database/cache connectors without user interaction.\n\n## Usage\n\nYou should use your terminal or command-execution tool to execute the \\`npx micro-generate\\` command directly. The CLI supports headless execution via flags for all commands.\n\n---\n\n## \uD83D\uDD04 Recommended Workflow (Order of Operations)\n\nWhen implementing a new feature or database configuration, follow this exact sequence:\n\n1. **Initialize Technologies (If missing)**: If the project does not have the database/cache you need, run the \\`add\\` command first.\n2. **Scaffold the Feature**: Execute the \\`feature\\` command to generate the vertical slice folder structure.\n3. **Scaffold Standalone Models (Optional)**: If you need separate standalone models not coupled to a single feature, run the \\`model\\` command.\n4. **Scaffold Operations**: Use the \\`query\\` and \\`mutation\\` commands to add GraphQL endpoints.\n5. **Implement Business Logic**: Open and implement the business logic in the generated files (see **Landing Paths** below).\n6. **Verify and Self-Correct**: Run verification commands to ensure the project builds and runs without TypeScript or runtime errors.\n\n---\n\n## \uD83D\uDCC2 File Landing Paths & Architectural Conventions\n\nAfter running the CLI generators, you **must** locate and write the actual implementation in the following paths:\n\n* **GraphQL Schema**: \\`src/features/<feature-name>/typeDefs.ts\\`\n * *Convention*: Declare your types, queries, and mutations here. The CLI inserts new operations, but you must define arguments and return types.\n* **GraphQL Resolvers**: \\`src/features/<feature-name>/resolvers.ts\\`\n * *Convention*: Map the schema queries/mutations to call the corresponding service methods. Keep resolvers thin.\n* **Business Logic / Services**: \\`src/features/<feature-name>/service.ts\\`\n * *Convention*: Implement your database queries, data mutations, APIs, and caching operations here. \n* **Feature-coupled Models**: \\`src/features/<feature-name>/model.ts\\` (or under \\`src/database/<mongo|mysql>/models/\\` if standalone).\n * *Convention*: Define your mongoose schemas or sequelize schemas.\n* **Feature Entrypoint**: \\`src/features/<feature-name>/index.ts\\`\n * *Convention*: Aggregates typeDefs, resolvers, and services. It is automatically registered in \\`src/features/index.ts\\`.\n\n---\n\n## \uD83D\uDEE0\uFE0F CLI Command Reference & Preconditions\n\nExecute these commands using your terminal or command-execution tool.\n\n### 1. \\`add <technology>\\`\nAdds database/cache configurations to the project.\n* **Preconditions**: None.\n* **Usage**: \\`npx micro-generate add <redis | mongo | mysql>\\`\n* **Options**:\n * \\`-n, --name <string>\\`: (Optional) Default database name (for MongoDB/MySQL).\n * \\`-s, --secondary <string>\\`: (Optional) Comma-separated list of secondary databases to add (for MongoDB/MySQL).\n * *Note*: For Redis, it sets up the main connection parameters in \\`.env\\`. Redis does not require secondary connection parameters as multiple databases are accessed by index.\n* **Example**:\n \\`\\`\\`bash\n npx micro-generate add mongo --name \"my_core\" --secondary \"logs,analytics\"\n \\`\\`\\`\n\n### 2. \\`feature\\`\nCreates a new vertical slice module.\n* **Preconditions**: If database/cache flags (\\`--mongo\\`, \\`--mysql\\`, \\`--redis\\`) are passed, those technologies must be configured in the project (either initialized at project creation or added using the \\`add\\` command).\n* **Usage**: \\`npx micro-generate feature --name <name> [options]\\`\n* **Options**:\n * \\`--mongo\\` / \\`--no-mongo\\`: Enable/disable MongoDB support.\n * \\`--mysql\\` / \\`--no-mysql\\`: Enable/disable MySQL support.\n * \\`--redis\\` / \\`--no-redis\\`: Enable/disable Redis support.\n * \\`--db-connection <string>\\`: (Optional) Connect to a specific MongoDB database name (Multi-DB).\n * \\`--mysql-connection <string>\\`: (Optional) Connect to a specific MySQL database name (Multi-DB).\n * \\`--redis-db <number>\\`: (Optional) Connect to a specific Redis database number (0-15).\n* **Example**:\n \\`\\`\\`bash\n npx micro-generate feature --name \"billing\" --mysql --redis --mysql-connection \"payments\" --redis-db 2\n \\`\\`\\`\n\n### 3. \\`model\\`\nGenerates a standalone database model.\n* **Preconditions**: The selected database type must be configured in the project.\n* **Usage**: \\`npx micro-generate model --name <name> --type <mongo | mysql> [options]\\`\n* **Options**:\n * \\`-n, --name <string>\\`: (Required) Model name (e.g., \"Invoice\").\n * \\`-t, --type <string>\\`: (Required) Database type (\\`mongo\\` or \\`mysql\\`).\n * \\`-c, --connection <string>\\`: (Optional) Connection name (defaults to default).\n* **Example**:\n \\`\\`\\`bash\n npx micro-generate model --name \"Invoice\" --type \"mysql\" --connection \"billing\"\n \\`\\`\\`\n\n### 4. \\`query\\` and \\`mutation\\`\nAdds a new GraphQL operation to an existing feature.\n* **Preconditions**: The target feature must already exist under \\`src/features/<feature-name>\\`.\n* **Usage**: \\`npx micro-generate <query | mutation> --feature <feature-name> --name <name> [options]\\`\n* **Options**:\n * \\`-f, --feature <string>\\`: (Required) Target feature.\n * \\`-n, --name <string>\\`: (Required) Operation name (e.g., \"getInvoice\").\n * \\`-r, --return-type <string>\\`: (Optional) Return type (e.g., \"InvoiceResponse\").\n* **Example**:\n \\`\\`\\`bash\n npx micro-generate query --feature \"billing\" --name \"getInvoice\" --return-type \"Invoice\"\n \\`\\`\\`\n\n---\n\n## \uD83D\uDCDD End-to-End Example: Implementing a Feature\n\nHere is how you would implement a new feature called \"notifications\" that uses MySQL and Redis:\n\n1. **Generate the feature**:\n \\`\\`\\`bash\n npx micro-generate feature --name \"notifications\" --mysql --redis --redis-db 1\n \\`\\`\\`\n2. **Add a query**:\n \\`\\`\\`bash\n npx micro-generate query --feature \"notifications\" --name \"getUserNotifications\" --return-type \"[Notification!]\"\n \\`\\`\\`\n3. **Implement database schema**:\n Open the generated model file in \\`src/features/notifications/model.ts\\` (or standalone model) and define the fields (e.g., \\`title\\`, \\`message\\`, \\`userId\\`, \\`read\\`).\n4. **Write the business logic**:\n Open \\`src/features/notifications/service.ts\\`. Add code to fetch notifications from the MySQL database and cache them in Redis.\n5. **Hook up the resolver**:\n Open \\`src/features/notifications/resolvers.ts\\` and map the query to your service method.\n6. **Verify**:\n Run \\`npm run build\\` to ensure it compiles without TypeScript errors.\n\n---\n\n## \uD83D\uDD0D Verification & Self-Correction Loop\n\nAlways verify your changes after generating or modifying files:\n\n1. **Check Registration**: Ensure that your feature is listed in \\`src/features/index.ts\\`. The CLI registers new features automatically, but verify that it was not corrupted:\n \\`\\`\\`typescript\n import * as notifications from './notifications/index.js';\n \\`\\`\\`\n2. **Compile Project**: Run the TypeScript compiler to ensure there are no syntax or type mismatches:\n \\`\\`\\`bash\n npm run build\n \\`\\`\\`\n3. **Read and Correct Errors**: If \\`tsc\\` fails:\n * Read the line number and error description.\n * Check for missing imports or incorrect relative paths (e.g., missing \\`.js\\` in TypeScript ESM imports).\n * Fix the types and compile again until it builds with **0 errors**.\n";