@aloma.io/integration-sdk 3.8.55 → 3.8.56

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.
package/build/cli.mjs CHANGED
@@ -111,20 +111,16 @@ program
111
111
  .option('--out <file>', 'output file path for the controller', 'src/controller/index.mts')
112
112
  .option('--resource <className>', 'Generate as a resource class with the specified class name (e.g., CompaniesResource)')
113
113
  .option('--multi-resource', 'Generate multiple resource files + main controller (requires multiple --spec files)')
114
+ .option('--controller-only', 'Generate only the controller file, do not create full project structure')
114
115
  .option('--no-build', 'Skip installing dependencies and building the project')
115
116
  .action(async (name, options) => {
116
117
  name = name.replace(/[\/\.]/gi, '');
117
118
  if (!name)
118
119
  throw new Error('name is empty');
119
- const target = `${process.cwd()}/${name}`;
120
120
  try {
121
121
  // Read and parse the OpenAPI spec
122
122
  const specContent = fs.readFileSync(options.spec, 'utf-8');
123
123
  const spec = OpenAPIToConnector.parseSpec(specContent);
124
- // Create the connector project structure
125
- fs.mkdirSync(target);
126
- console.log('Creating connector project...');
127
- extract({ ...options, target, name });
128
124
  // Generate the controller from OpenAPI spec
129
125
  const generator = new OpenAPIToConnector(spec, name);
130
126
  let controllerCode;
@@ -136,44 +132,66 @@ program
136
132
  console.log('Generating controller from OpenAPI specification...');
137
133
  controllerCode = generator.generateController();
138
134
  }
139
- // Write the generated controller
140
- const controllerPath = `${target}/${options.out}`;
141
- fs.mkdirSync(path.dirname(controllerPath), { recursive: true });
142
- fs.writeFileSync(controllerPath, controllerCode);
143
- console.log('Generating keys...');
144
- await generateKeys({ target });
145
- if (options.build !== false) {
146
- console.log('Installing dependencies...');
147
- await exec(`cd ${target}; yarn --ignore-engines`);
148
- console.log('Building...');
149
- await exec(`cd ${target}; yarn build`);
135
+ if (options.controllerOnly) {
136
+ // Controller-only mode: just generate the controller file
137
+ const controllerPath = path.resolve(options.out);
138
+ fs.mkdirSync(path.dirname(controllerPath), { recursive: true });
139
+ fs.writeFileSync(controllerPath, controllerCode);
140
+ console.log(`\nāœ… Success! Generated controller from OpenAPI specification
141
+ šŸ“ Connector name: ${name}
142
+ šŸ“Š Found ${generator.getOperationsCount()} operations
143
+ šŸ“„ Controller file: ${controllerPath}
144
+
145
+ Controller file generated successfully! You can now use it in your existing project.`);
150
146
  }
151
- const nextSteps = options.build !== false
152
- ? `Next steps:
147
+ else {
148
+ // Full project mode: create complete project structure
149
+ const target = `${process.cwd()}/${name}`;
150
+ // Create the connector project structure
151
+ fs.mkdirSync(target);
152
+ console.log('Creating connector project...');
153
+ extract({ ...options, target, name });
154
+ // Write the generated controller
155
+ const controllerPath = `${target}/${options.out}`;
156
+ fs.mkdirSync(path.dirname(controllerPath), { recursive: true });
157
+ fs.writeFileSync(controllerPath, controllerCode);
158
+ console.log('Generating keys...');
159
+ await generateKeys({ target });
160
+ if (options.build !== false) {
161
+ console.log('Installing dependencies...');
162
+ await exec(`cd ${target}; yarn --ignore-engines`);
163
+ console.log('Building...');
164
+ await exec(`cd ${target}; yarn build`);
165
+ }
166
+ const nextSteps = options.build !== false
167
+ ? `Next steps:
153
168
  1.) Add the connector to a workspace
154
169
  2.) Edit ./${name}/.env and insert the registration token
155
170
  3.) Implement the actual API calls in each method in ${options.out}
156
171
  4.) Start the connector with cd ./${name}/; yarn start`
157
- : `Next steps:
172
+ : `Next steps:
158
173
  1.) Install dependencies: cd ./${name}/ && yarn --ignore-engines
159
174
  2.) Implement the actual API calls in each method in ${options.out}
160
175
  3.) Build the project: yarn build
161
176
  4.) Add the connector to a workspace
162
177
  5.) Edit ./${name}/.env and insert the registration token
163
178
  6.) Start the connector: yarn start`;
164
- console.log(`
165
- āœ… Success! Generated connector from OpenAPI specification
179
+ console.log(`\nāœ… Success! Generated connector from OpenAPI specification
166
180
  šŸ“ Connector name: ${name}
167
181
  šŸ“Š Found ${generator.getOperationsCount()} operations
168
182
  šŸ“„ Controller generated: ${options.out}
169
183
 
170
184
  ${nextSteps}`);
185
+ }
171
186
  }
172
187
  catch (error) {
173
188
  console.error('āŒ Error:', error instanceof Error ? error.message : 'Unknown error');
174
- // Clean up on error
175
- if (fs.existsSync(target)) {
176
- fs.rmSync(target, { recursive: true, force: true });
189
+ // Clean up on error (only if we created a project directory)
190
+ if (!options.controllerOnly) {
191
+ const target = `${process.cwd()}/${name}`;
192
+ if (fs.existsSync(target)) {
193
+ fs.rmSync(target, { recursive: true, force: true });
194
+ }
177
195
  }
178
196
  process.exit(1);
179
197
  }
@@ -223,6 +241,7 @@ program
223
241
  extract({ ...options, target, name });
224
242
  // Generate each resource
225
243
  const resources = [];
244
+ const parsedResourceSpecs = [];
226
245
  let baseUrl = options.baseUrl;
227
246
  for (const { className, specFile } of resourceSpecs) {
228
247
  console.log(`Generating ${className} from ${specFile}...`);
@@ -242,12 +261,13 @@ program
242
261
  fs.mkdirSync(path.dirname(resourcePath), { recursive: true });
243
262
  fs.writeFileSync(resourcePath, resourceCode);
244
263
  resources.push({ className, fileName });
264
+ parsedResourceSpecs.push({ fileName, spec });
245
265
  }
246
266
  // Generate the main controller
247
267
  console.log('Generating main controller...');
248
268
  const firstSpec = OpenAPIToConnector.parseSpec(fs.readFileSync(resourceSpecs[0].specFile, 'utf-8'));
249
269
  const mainGenerator = new OpenAPIToConnector(firstSpec, name);
250
- const mainControllerCode = mainGenerator.generateMainController(resources);
270
+ const mainControllerCode = mainGenerator.generateMainController(resources, parsedResourceSpecs);
251
271
  // Write the main controller
252
272
  const controllerPath = `${target}/src/controller/index.mts`;
253
273
  fs.writeFileSync(controllerPath, mainControllerCode);
@@ -296,12 +316,16 @@ program
296
316
  if (!fs.existsSync(target)) {
297
317
  throw new Error(`Project path does not exist: ${target}`);
298
318
  }
319
+ const controllerPath = `${target}/src/controller/index.mts`;
320
+ if (!fs.existsSync(controllerPath)) {
321
+ throw new Error(`Controller file not found: ${controllerPath}. This might not be a multi-resource connector project.`);
322
+ }
299
323
  try {
300
324
  console.log(`Adding ${options.className} resource to existing project...`);
301
325
  // Read and parse the OpenAPI spec
302
326
  const specContent = fs.readFileSync(options.spec, 'utf-8');
303
327
  const spec = OpenAPIToConnector.parseSpec(specContent);
304
- // Generate the resource class
328
+ // Generate the resource functions file (new function-based pattern)
305
329
  const generator = new OpenAPIToConnector(spec, 'Resource');
306
330
  const resourceCode = generator.generateResourceClass(options.className);
307
331
  // Write the resource file
@@ -309,18 +333,83 @@ program
309
333
  const resourcePath = `${target}/src/resources/${fileName}.mts`;
310
334
  fs.mkdirSync(path.dirname(resourcePath), { recursive: true });
311
335
  fs.writeFileSync(resourcePath, resourceCode);
312
- console.log(`āœ… Resource ${options.className} added successfully at ${resourcePath}`);
313
- console.log('\nāš ļø You need to manually update the main controller to include this new resource:');
314
- console.log(`1.) Add import: import ${options.className} from '../resources/${fileName}.mjs';`);
315
- console.log(`2.) Add property: ${fileName}!: ${options.className};`);
316
- console.log(`3.) Add initialization in start(): this.${fileName} = new ${options.className}(this);`);
336
+ // Update the main controller to include the new resource
337
+ console.log('Updating main controller...');
338
+ let controllerContent = fs.readFileSync(controllerPath, 'utf-8');
339
+ // Add import
340
+ const importStatement = `import * as ${fileName}Functions from '../resources/${fileName}.mjs';`;
341
+ if (!controllerContent.includes(importStatement)) {
342
+ // Find the last import and add after it
343
+ const importRegex = /^import.*from.*?;$/gm;
344
+ const imports = controllerContent.match(importRegex);
345
+ if (imports && imports.length > 0) {
346
+ const lastImport = imports[imports.length - 1];
347
+ controllerContent = controllerContent.replace(lastImport, `${lastImport}\n${importStatement}`);
348
+ }
349
+ else {
350
+ // If no imports found, add at the beginning
351
+ controllerContent = `${importStatement}\n\n${controllerContent}`;
352
+ }
353
+ }
354
+ // Add property declaration
355
+ const propertyDeclaration = ` ${fileName}: any = {};`;
356
+ if (!controllerContent.includes(propertyDeclaration)) {
357
+ // Find the existing properties and add the new one
358
+ const propertyRegex = /^ \w+: any = \{\};$/gm;
359
+ const properties = controllerContent.match(propertyRegex);
360
+ if (properties && properties.length > 0) {
361
+ const lastProperty = properties[properties.length - 1];
362
+ controllerContent = controllerContent.replace(lastProperty, `${lastProperty}\n${propertyDeclaration}`);
363
+ }
364
+ else {
365
+ // Add after class declaration
366
+ controllerContent = controllerContent.replace(/^export default class Controller extends AbstractController \{$/gm, `export default class Controller extends AbstractController {\n${propertyDeclaration}`);
367
+ }
368
+ }
369
+ // Add binding in start() method
370
+ const bindingStatement = ` this.bindResourceFunctions('${fileName}', ${fileName}Functions);`;
371
+ if (!controllerContent.includes(bindingStatement)) {
372
+ // Find the existing bindings and add the new one
373
+ const bindingRegex = /^ this\.bindResourceFunctions\(.*?\);$/gm;
374
+ const bindings = controllerContent.match(bindingRegex);
375
+ if (bindings && bindings.length > 0) {
376
+ const lastBinding = bindings[bindings.length - 1];
377
+ controllerContent = controllerContent.replace(lastBinding, `${lastBinding}\n${bindingStatement}`);
378
+ }
379
+ }
380
+ // Write the updated controller
381
+ fs.writeFileSync(controllerPath, controllerContent);
382
+ // Generate exposed methods for the new resource
383
+ console.log('Generating exposed methods for the new resource...');
384
+ const resources = [{ className: options.className, fileName }];
385
+ const resourceSpecs = [{ fileName, spec }];
386
+ // Create a temporary generator to generate just the exposed methods for this resource
387
+ const tempGenerator = new OpenAPIToConnector(spec, 'temp');
388
+ const exposedMethods = tempGenerator.generateExposedResourceMethods(resources, resourceSpecs);
389
+ // Add the exposed methods to the controller before the closing brace
390
+ if (exposedMethods.trim()) {
391
+ controllerContent = fs.readFileSync(controllerPath, 'utf-8');
392
+ // Find the last method and add the new exposed methods
393
+ const lastBrace = controllerContent.lastIndexOf('}');
394
+ if (lastBrace !== -1) {
395
+ const beforeBrace = controllerContent.substring(0, lastBrace);
396
+ const afterBrace = controllerContent.substring(lastBrace);
397
+ const updatedContent = `${beforeBrace}\n${exposedMethods}\n${afterBrace}`;
398
+ fs.writeFileSync(controllerPath, updatedContent);
399
+ }
400
+ }
401
+ console.log(`āœ… Resource ${options.className} added successfully!`);
402
+ console.log(`šŸ“„ Resource functions: ${resourcePath}`);
403
+ console.log(`šŸŽ›ļø Controller updated: ${controllerPath}`);
404
+ console.log(`\nšŸŽ‰ The new resource is fully integrated and ready to use!`);
317
405
  if (options.build !== false) {
318
- console.log('\nBuilding project...');
406
+ console.log('\nšŸ”Ø Building project...');
319
407
  await exec(`cd "${target}"; yarn build`);
408
+ console.log('āœ… Build complete!');
320
409
  }
321
410
  }
322
411
  catch (error) {
323
- console.error('Error adding resource:', error.message);
412
+ console.error('āŒ Error adding resource:', error.message);
324
413
  process.exit(1);
325
414
  }
326
415
  });
@@ -20,10 +20,6 @@ export declare class OpenAPIToConnector {
20
20
  * Generate method name from operation info
21
21
  */
22
22
  private generateMethodName;
23
- /**
24
- * Generate JSDoc comment for an operation
25
- */
26
- private generateJSDoc;
27
23
  /**
28
24
  * Get the number of operations in the OpenAPI spec
29
25
  */
@@ -32,31 +28,112 @@ export declare class OpenAPIToConnector {
32
28
  * Generate method signature with options object
33
29
  */
34
30
  private generateMethodSignature;
31
+ /**
32
+ * Resolve a schema reference to a TypeScript type name
33
+ */
34
+ private resolveSchemaRef;
35
+ /**
36
+ * Sanitize a name to be a valid TypeScript identifier
37
+ */
38
+ private sanitizeTypeName;
39
+ /**
40
+ * Get TypeScript type from schema object
41
+ */
42
+ private getTypeFromSchema;
43
+ /**
44
+ * Get TypeScript type for request body
45
+ */
46
+ private getRequestBodyType;
47
+ /**
48
+ * Add request body properties directly to options array (flatten the body)
49
+ */
50
+ private addRequestBodyProperties;
51
+ /**
52
+ * Get TypeScript type for response
53
+ */
54
+ private getResponseType;
35
55
  /**
36
56
  * Get TypeScript type for a parameter based on its schema
37
57
  */
38
58
  private getParameterType;
39
59
  /**
40
- * Generate method implementation code
60
+ * Generate method implementation code for controller methods with discrete path parameters
41
61
  */
42
- private generateMethodImplementation;
62
+ private generateControllerMethodImplementation;
63
+ /**
64
+ * Generate method implementation for resource functions (using this.api instead of this.controller)
65
+ */
66
+ private generateResourceFunctionImplementation;
67
+ /**
68
+ * Generate exposed resource methods for API introspection
69
+ */
70
+ generateExposedResourceMethods(resources: Array<{
71
+ className: string;
72
+ fileName: string;
73
+ }>, resourceSpecs?: Array<{
74
+ fileName: string;
75
+ spec: OpenAPIV3.Document;
76
+ }>): string;
43
77
  /**
44
- * Generate proper import paths with .mjs extensions for TypeScript module resolution
78
+ * Generate parameter call for a specific operation based on path parameters
45
79
  */
46
- private generateImportPath;
80
+ private generateParameterCallForOperation;
47
81
  /**
48
- * Generate a resource class (does NOT extend AbstractController, receives controller reference)
82
+ * Generate TypeScript interface from OpenAPI schema
83
+ */
84
+ private generateInterfaceFromSchema;
85
+ /**
86
+ * Generate all TypeScript interfaces from OpenAPI components
87
+ */
88
+ private generateAllInterfaces;
89
+ /**
90
+ * Generate detailed JSDoc with schema field information
91
+ */
92
+ private generateDetailedJSDoc;
93
+ /**
94
+ * Add flattened request body documentation to JSDoc
95
+ */
96
+ private addFlattenedBodyDocumentation;
97
+ /**
98
+ * Add detailed schema field information to JSDoc
99
+ */
100
+ private addSchemaDetails;
101
+ /**
102
+ * Collect all schema types used in operations (including nested references)
103
+ */
104
+ private collectUsedSchemaTypes;
105
+ /**
106
+ * Collect types from request body schema
107
+ */
108
+ private collectTypesFromRequestBody;
109
+ /**
110
+ * Collect types from response schemas
111
+ */
112
+ private collectTypesFromResponses;
113
+ /**
114
+ * Collect types from a schema object
115
+ */
116
+ private collectTypesFromSchema;
117
+ /**
118
+ * Generate type imports for used schema types
119
+ */
120
+ private generateTypeImports;
121
+ /**
122
+ * Generate a resource file with exported functions (new pattern for proper introspection)
49
123
  */
50
124
  generateResourceClass(className: string): string;
51
125
  /**
52
- * Generate a main controller that composes multiple resources
126
+ * Generate a main controller that composes multiple resources using function binding
53
127
  */
54
128
  generateMainController(resources: Array<{
55
129
  className: string;
56
130
  fileName: string;
131
+ }>, resourceSpecs?: Array<{
132
+ fileName: string;
133
+ spec: OpenAPIV3.Document;
57
134
  }>): string;
58
135
  /**
59
- * Generate the connector controller code
136
+ * Generate the connector controller code with improved pattern
60
137
  */
61
138
  generateController(): string;
62
139
  }