@aifabrix/builder 2.39.3 → 2.40.2

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 (117) hide show
  1. package/.cursor/rules/project-rules.mdc +6 -6
  2. package/README.md +3 -3
  3. package/babel.config.js +6 -0
  4. package/integration/hubspot/README.md +53 -141
  5. package/integration/hubspot/application.yaml +37 -0
  6. package/integration/hubspot/env.template +2 -11
  7. package/integration/hubspot/hubspot-deploy.json +1 -0
  8. package/integration/hubspot/test.js +5 -5
  9. package/jest.config.manual.js +29 -0
  10. package/lib/api/credentials.api.js +5 -5
  11. package/lib/api/deployments.api.js +2 -2
  12. package/lib/api/pipeline.api.js +17 -17
  13. package/lib/api/wizard.api.js +2 -2
  14. package/lib/app/config.js +11 -6
  15. package/lib/app/deploy-config.js +13 -16
  16. package/lib/app/deploy.js +29 -22
  17. package/lib/app/display.js +1 -1
  18. package/lib/app/dockerfile.js +11 -12
  19. package/lib/app/helpers.js +51 -13
  20. package/lib/app/index.js +14 -2
  21. package/lib/app/prompts.js +37 -45
  22. package/lib/app/push.js +8 -11
  23. package/lib/app/readme.js +16 -12
  24. package/lib/app/register.js +1 -1
  25. package/lib/app/run-helpers.js +31 -22
  26. package/lib/app/run.js +44 -5
  27. package/lib/app/show-display.js +104 -44
  28. package/lib/app/show.js +123 -43
  29. package/lib/build/index.js +11 -18
  30. package/lib/cli/setup-app.js +36 -29
  31. package/lib/cli/setup-auth.js +19 -15
  32. package/lib/cli/setup-credential-deployment.js +3 -1
  33. package/lib/cli/setup-external-system.js +35 -16
  34. package/lib/cli/setup-infra.js +45 -23
  35. package/lib/cli/setup-utility.js +85 -31
  36. package/lib/commands/app-logs.js +28 -20
  37. package/lib/commands/app.js +30 -26
  38. package/lib/commands/auth-status.js +36 -3
  39. package/lib/commands/convert.js +202 -0
  40. package/lib/commands/credential-list.js +78 -17
  41. package/lib/commands/datasource.js +24 -24
  42. package/lib/commands/deployment-list.js +13 -6
  43. package/lib/commands/up-common.js +80 -42
  44. package/lib/commands/up-dataplane.js +15 -14
  45. package/lib/commands/up-miso.js +15 -14
  46. package/lib/commands/upload.js +163 -0
  47. package/lib/commands/wizard-core.js +5 -4
  48. package/lib/core/diff.js +84 -9
  49. package/lib/core/key-generator.js +9 -12
  50. package/lib/core/secrets-docker-env.js +2 -2
  51. package/lib/core/secrets.js +3 -2
  52. package/lib/core/templates.js +2 -2
  53. package/lib/datasource/deploy.js +2 -1
  54. package/lib/deployment/deployer.js +76 -48
  55. package/lib/external-system/delete.js +0 -1
  56. package/lib/external-system/deploy-helpers.js +5 -6
  57. package/lib/external-system/deploy.js +7 -2
  58. package/lib/external-system/download-helpers.js +4 -4
  59. package/lib/external-system/download.js +11 -10
  60. package/lib/external-system/generator.js +19 -17
  61. package/lib/external-system/test.js +10 -15
  62. package/lib/generator/builders.js +1 -1
  63. package/lib/generator/external-controller-manifest.js +26 -29
  64. package/lib/generator/external-schema-utils.js +6 -18
  65. package/lib/generator/external.js +32 -27
  66. package/lib/generator/github.js +1 -1
  67. package/lib/generator/helpers.js +12 -19
  68. package/lib/generator/index.js +15 -15
  69. package/lib/generator/parse-image.js +35 -0
  70. package/lib/generator/split-readme.js +105 -0
  71. package/lib/generator/split-variables.js +149 -0
  72. package/lib/generator/split.js +86 -246
  73. package/lib/generator/wizard.js +51 -70
  74. package/lib/schema/application-schema.json +4 -4
  75. package/lib/schema/external-datasource.schema.json +5 -0
  76. package/lib/schema/external-system.schema.json +10 -0
  77. package/lib/utils/app-config-resolver.js +52 -0
  78. package/lib/utils/app-register-api.js +1 -1
  79. package/lib/utils/app-register-auth.js +1 -1
  80. package/lib/utils/app-register-config.js +16 -23
  81. package/lib/utils/app-register-validator.js +2 -2
  82. package/lib/utils/cli-utils.js +47 -3
  83. package/lib/utils/config-format.js +154 -0
  84. package/lib/utils/config-paths.js +19 -52
  85. package/lib/utils/config-tokens.js +1 -0
  86. package/lib/utils/docker-build.js +71 -94
  87. package/lib/utils/dockerfile-utils.js +1 -1
  88. package/lib/utils/env-copy.js +4 -4
  89. package/lib/utils/env-ports.js +2 -2
  90. package/lib/utils/error-formatter.js +1 -1
  91. package/lib/utils/error-formatters/validation-errors.js +1 -1
  92. package/lib/utils/external-readme.js +12 -5
  93. package/lib/utils/external-system-test-helpers.js +2 -0
  94. package/lib/utils/health-check.js +55 -66
  95. package/lib/utils/image-version.js +12 -21
  96. package/lib/utils/paths.js +45 -66
  97. package/lib/utils/port-resolver.js +8 -8
  98. package/lib/utils/schema-loader.js +22 -0
  99. package/lib/utils/schema-resolver.js +23 -33
  100. package/lib/utils/secrets-helpers.js +7 -7
  101. package/lib/utils/secrets-utils.js +10 -12
  102. package/lib/utils/template-helpers.js +13 -13
  103. package/lib/utils/token-manager.js +20 -2
  104. package/lib/utils/variable-transformer.js +2 -2
  105. package/lib/validation/validate-display.js +3 -4
  106. package/lib/validation/validate.js +34 -28
  107. package/lib/validation/validator.js +50 -30
  108. package/package.json +4 -1
  109. package/templates/README.md +1 -1
  110. package/templates/applications/README.md.hbs +3 -3
  111. package/templates/applications/miso-controller/env.template +3 -1
  112. package/templates/external-system/README.md.hbs +4 -4
  113. package/templates/external-system/external-system.json.hbs +1 -16
  114. package/integration/hubspot/variables.yaml +0 -17
  115. /package/templates/applications/dataplane/{variables.yaml → application.yaml} +0 -0
  116. /package/templates/applications/keycloak/{variables.yaml → application.yaml} +0 -0
  117. /package/templates/applications/miso-controller/{variables.yaml → application.yaml} +0 -0
@@ -290,7 +290,7 @@ function addHealthCheckToDeployment(deployment, variables) {
290
290
  * @param {Object|null} rbac - RBAC configuration
291
291
  */
292
292
  function addRolesAndPermissions(deployment, variables, rbac) {
293
- // Priority: variables.yaml > rbac.yaml
293
+ // Priority: application.yaml > rbac.yaml
294
294
  if (variables.roles) {
295
295
  deployment.roles = variables.roles;
296
296
  } else if (rbac && rbac.roles) {
@@ -11,6 +11,7 @@
11
11
 
12
12
  const path = require('path');
13
13
  const { detectAppType } = require('../utils/paths');
14
+ const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
14
15
  const { loadSystemFile, loadDatasourceFiles } = require('./external');
15
16
  const { loadVariables, loadRbac } = require('./helpers');
16
17
 
@@ -42,18 +43,18 @@ function mergeRbacIntoSystemJson(systemJson, rbac) {
42
43
  * @param {Object} options - Options with optional appPath
43
44
  * @returns {Promise<string>} Application path
44
45
  */
45
- async function resolveAppPath(appName, options) {
46
- if (options.appPath) {
46
+ async function resolveAppPath(appName, options = {}) {
47
+ if (options && options.appPath) {
47
48
  return options.appPath;
48
49
  }
49
- const detected = await detectAppType(appName, { type: 'external' });
50
+ const detected = await detectAppType(appName);
50
51
  return detected.appPath;
51
52
  }
52
53
 
53
54
  /**
54
55
  * Extracts app metadata from variables
55
56
  * @function extractAppMetadata
56
- * @param {Object} variables - Parsed variables.yaml
57
+ * @param {Object} variables - Parsed application config
57
58
  * @param {string} appName - Application name
58
59
  * @returns {Object} App metadata { appKey, displayName, description }
59
60
  */
@@ -98,59 +99,55 @@ async function loadSystemWithRbac(appPath, schemaBasePath, systemFile) {
98
99
  * const manifest = await generateControllerManifest('my-hubspot');
99
100
  * // Returns: { key, displayName, description, type: "external", system: {...}, dataSources: [...] }
100
101
  */
102
+ function normalizeSchemaBasePath(schemaBasePath, appPath, appName) {
103
+ const base = path.normalize(schemaBasePath || './').replace(/[/\\]+$/, '');
104
+ return base === path.join('integration', appName) ? './' : (schemaBasePath || './');
105
+ }
106
+
101
107
  async function generateControllerManifest(appName, options = {}) {
102
108
  if (!appName || typeof appName !== 'string') {
103
109
  throw new Error('App name is required and must be a string');
104
110
  }
105
-
106
111
  const appPath = await resolveAppPath(appName, options);
107
- const variablesPath = path.join(appPath, 'variables.yaml');
108
- const { parsed: variables } = loadVariables(variablesPath);
109
-
112
+ const { parsed: variables } = loadVariables(resolveApplicationConfigPath(appPath));
110
113
  if (!variables.externalIntegration) {
111
- throw new Error('externalIntegration block not found in variables.yaml');
114
+ throw new Error('externalIntegration block not found in application.yaml');
112
115
  }
113
-
114
116
  const metadata = extractAppMetadata(variables, appName);
115
- const schemaBasePath = variables.externalIntegration.schemaBasePath || './';
117
+ const schemaBasePath = normalizeSchemaBasePath(
118
+ variables.externalIntegration.schemaBasePath,
119
+ appPath,
120
+ appName
121
+ );
116
122
  const systemFiles = variables.externalIntegration.systems || [];
117
-
118
123
  if (systemFiles.length === 0) {
119
124
  throw new Error('No system files specified in externalIntegration.systems');
120
125
  }
121
-
122
- const systemJson = await loadSystemWithRbac(appPath, schemaBasePath, systemFiles[0]);
123
- const datasourceFiles = variables.externalIntegration.dataSources || [];
124
- const datasourceJsons = await loadDatasourceFiles(appPath, schemaBasePath, datasourceFiles);
125
-
126
+ const [systemJson, datasourceJsons] = await Promise.all([
127
+ loadSystemWithRbac(appPath, schemaBasePath, systemFiles[0]),
128
+ loadDatasourceFiles(appPath, schemaBasePath, variables.externalIntegration.dataSources || [])
129
+ ]);
126
130
  const appVersion = variables.app?.version || variables.externalIntegration?.version || '1.0.0';
127
-
128
- // Build externalIntegration block (required by application schema for type: "external")
129
131
  const externalIntegration = {
130
- schemaBasePath: schemaBasePath,
132
+ schemaBasePath,
131
133
  systems: systemFiles,
132
- dataSources: datasourceFiles,
133
- autopublish: variables.externalIntegration.autopublish !== false, // default true
134
+ dataSources: variables.externalIntegration.dataSources || [],
135
+ autopublish: variables.externalIntegration.autopublish !== false,
134
136
  version: appVersion
135
137
  };
136
-
137
- const manifest = {
138
+ return {
138
139
  key: metadata.appKey,
139
140
  displayName: metadata.displayName,
140
141
  description: metadata.description,
141
142
  type: 'external',
142
143
  version: appVersion,
143
- externalIntegration: externalIntegration,
144
- // Inline system and dataSources for atomic deployment (optional but recommended)
144
+ externalIntegration,
145
145
  system: systemJson,
146
146
  dataSources: datasourceJsons,
147
- // Explicitly set to false to satisfy conditional schema requirements
148
147
  requiresDatabase: false,
149
148
  requiresRedis: false,
150
149
  requiresStorage: false
151
150
  };
152
-
153
- return manifest;
154
151
  }
155
152
 
156
153
  module.exports = {
@@ -56,18 +56,6 @@ function getSystemKey(application) {
56
56
  return systemKey;
57
57
  }
58
58
 
59
- /**
60
- * Writes JSON file with formatting
61
- * @async
62
- * @function writeJsonFile
63
- * @param {string} filePath - File path
64
- * @param {Object} data - JSON data
65
- * @returns {Promise<void>} Resolves when file is written
66
- */
67
- async function writeJsonFile(filePath, data) {
68
- await fs.promises.writeFile(filePath, JSON.stringify(data, null, 2), 'utf8');
69
- }
70
-
71
59
  /**
72
60
  * Resolves datasource entity type
73
61
  * @function getDatasourceEntityType
@@ -104,7 +92,7 @@ function getDatasourceFileName(systemKey, datasource, index) {
104
92
  } else {
105
93
  datasourceKeyOnly = getDatasourceEntityType(datasource, index);
106
94
  }
107
- return `${systemKey}-datasource-${datasourceKeyOnly}.json`;
95
+ return `${systemKey}-datasource-${datasourceKeyOnly}.yaml`;
108
96
  }
109
97
 
110
98
  /**
@@ -122,14 +110,14 @@ async function writeDatasourceFiles(outputDir, systemKey, dataSources) {
122
110
  const datasource = dataSources[i];
123
111
  const datasourceFileName = getDatasourceFileName(systemKey, datasource, i);
124
112
  const datasourceFilePath = path.join(outputDir, datasourceFileName);
125
- await writeJsonFile(datasourceFilePath, datasource);
113
+ await writeYamlFile(datasourceFilePath, datasource, { indent: 2, lineWidth: 120, noRefs: true });
126
114
  datasourceFileNames.push(datasourceFileName);
127
115
  }
128
116
  return datasourceFileNames;
129
117
  }
130
118
 
131
119
  /**
132
- * Builds variables.yaml content for external integrations
120
+ * Builds application config content for external integrations
133
121
  * @function buildExternalVariables
134
122
  * @param {string} systemKey - System key
135
123
  * @param {Object} application - Application schema
@@ -187,14 +175,14 @@ async function writeYamlFile(filePath, data, options) {
187
175
  * @returns {Promise<Object>} Paths to generated files
188
176
  */
189
177
  async function writeSplitExternalSchemaFiles({ outputDir, systemKey, application, dataSources, version }) {
190
- const systemFileName = `${systemKey}-system.json`;
178
+ const systemFileName = `${systemKey}-system.yaml`;
191
179
  const systemFilePath = path.join(outputDir, systemFileName);
192
- await writeJsonFile(systemFilePath, application);
180
+ await writeYamlFile(systemFilePath, application, { indent: 2, lineWidth: 120, noRefs: true });
193
181
 
194
182
  const datasourceFileNames = await writeDatasourceFiles(outputDir, systemKey, dataSources);
195
183
  const variables = buildExternalVariables(systemKey, application, systemFileName, datasourceFileNames, version);
196
184
 
197
- const variablesPath = path.join(outputDir, 'variables.yaml');
185
+ const variablesPath = path.join(outputDir, 'application.yaml');
198
186
  await writeYamlFile(variablesPath, variables, { indent: 2, lineWidth: 120, noRefs: true });
199
187
 
200
188
  const envTemplatePath = path.join(outputDir, 'env.template');
@@ -12,6 +12,8 @@ const fs = require('fs');
12
12
  const path = require('path');
13
13
  const Ajv = require('ajv');
14
14
  const { detectAppType, getDeployJsonPath } = require('../utils/paths');
15
+ const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
16
+ const { loadConfigFile } = require('../utils/config-format');
15
17
  const { loadVariables, loadRbac } = require('./helpers');
16
18
  const {
17
19
  parseApplicationSchema,
@@ -41,26 +43,29 @@ const {
41
43
  function resolveSystemFilePath(variables, appPath, appName) {
42
44
  const systemFileName = variables.externalIntegration.systems && variables.externalIntegration.systems.length > 0
43
45
  ? variables.externalIntegration.systems[0]
44
- : `${appName}-system.json`;
46
+ : `${appName}-system.yaml`;
45
47
 
46
- const schemaBasePath = variables.externalIntegration.schemaBasePath || './';
48
+ let schemaBasePath = variables.externalIntegration.schemaBasePath || './';
49
+ const normalizedBase = path.normalize(schemaBasePath).replace(/[/\\]+$/, '');
50
+ if (normalizedBase === path.join('integration', appName)) {
51
+ schemaBasePath = './';
52
+ }
47
53
  const systemFilePath = path.isAbsolute(schemaBasePath)
48
54
  ? path.join(schemaBasePath, systemFileName)
49
55
  : path.join(appPath, schemaBasePath, systemFileName);
50
56
 
51
- // Support both old and new naming for backward compatibility
57
+ // Support both .yaml and legacy .json for backward compatibility
52
58
  if (!fs.existsSync(systemFilePath)) {
53
- // Try old naming format
54
- const oldSystemFileName = systemFileName.replace(/-system\.json$/, '-deploy.json');
55
- const oldSystemFilePath = path.isAbsolute(schemaBasePath)
56
- ? path.join(schemaBasePath, oldSystemFileName)
57
- : path.join(appPath, schemaBasePath, oldSystemFileName);
58
-
59
- if (fs.existsSync(oldSystemFilePath)) {
60
- return oldSystemFilePath;
59
+ const altFileName = systemFileName.replace(/-system\.yaml$/, '-system.json').replace(/-system\.yml$/, '-system.json');
60
+ const altSystemFilePath = path.isAbsolute(schemaBasePath)
61
+ ? path.join(schemaBasePath, altFileName)
62
+ : path.join(appPath, schemaBasePath, altFileName);
63
+
64
+ if (fs.existsSync(altSystemFilePath)) {
65
+ return altSystemFilePath;
61
66
  }
62
67
 
63
- throw new Error(`External system file not found: ${systemFilePath} (also checked: ${oldSystemFilePath}). Please create it first.`);
68
+ throw new Error(`External system file not found: ${systemFilePath} (also checked: ${altSystemFilePath}). Please create it first.`);
64
69
  }
65
70
 
66
71
  return systemFilePath;
@@ -87,15 +92,14 @@ function mergeRbacIntoSystemJson(systemJson, rbac) {
87
92
  }
88
93
 
89
94
  /**
90
- * Loads and parses system file
95
+ * Loads and parses system file (YAML or JSON)
91
96
  * @async
92
97
  * @function loadSystemFileContent
93
98
  * @param {string} systemFilePath - System file path
94
- * @returns {Promise<Object>} Parsed system JSON
99
+ * @returns {Promise<Object>} Parsed system config object
95
100
  */
96
101
  async function loadSystemFileContent(systemFilePath) {
97
- const systemContent = await fs.promises.readFile(systemFilePath, 'utf8');
98
- return JSON.parse(systemContent);
102
+ return loadConfigFile(systemFilePath);
99
103
  }
100
104
 
101
105
  async function generateExternalSystemDeployJson(appName, appPath) {
@@ -103,11 +107,11 @@ async function generateExternalSystemDeployJson(appName, appPath) {
103
107
  throw new Error('App name is required and must be a string');
104
108
  }
105
109
 
106
- const variablesPath = path.join(appPath, 'variables.yaml');
107
- const { parsed: variables } = loadVariables(variablesPath);
110
+ const configPath = resolveApplicationConfigPath(appPath);
111
+ const { parsed: variables } = loadVariables(configPath);
108
112
 
109
113
  if (!variables.externalIntegration) {
110
- throw new Error('externalIntegration block not found in variables.yaml');
114
+ throw new Error('externalIntegration block not found in application.yaml');
111
115
  }
112
116
 
113
117
  const systemFilePath = resolveSystemFilePath(variables, appPath, appName);
@@ -141,11 +145,13 @@ async function loadSystemFile(appPath, schemaBasePath, systemFileName) {
141
145
  : path.join(appPath, schemaBasePath, systemFileName);
142
146
 
143
147
  if (!fs.existsSync(systemFilePath)) {
144
- throw new Error(`System file not found: ${systemFilePath}`);
148
+ const hint = systemFileName.endsWith('-deploy.json')
149
+ ? ' Use the system definition file (e.g. <app>-system.yaml) in externalIntegration.systems, not the deploy manifest (-deploy.json).'
150
+ : '';
151
+ throw new Error(`System file not found: ${systemFilePath}.${hint}`);
145
152
  }
146
153
 
147
- const systemContent = await fs.promises.readFile(systemFilePath, 'utf8');
148
- const systemJson = JSON.parse(systemContent);
154
+ const systemJson = loadConfigFile(systemFilePath);
149
155
 
150
156
  // Load rbac.yaml from app directory and merge if present
151
157
  const rbacPath = path.join(appPath, 'rbac.yaml');
@@ -183,8 +189,7 @@ async function loadDatasourceFiles(appPath, schemaBasePath, datasourceFiles) {
183
189
  throw new Error(`Datasource file not found: ${datasourcePath}`);
184
190
  }
185
191
 
186
- const datasourceContent = await fs.promises.readFile(datasourcePath, 'utf8');
187
- const datasourceJson = JSON.parse(datasourceContent);
192
+ const datasourceJson = loadConfigFile(datasourcePath);
188
193
  datasourceJsons.push(datasourceJson);
189
194
  }
190
195
 
@@ -297,11 +302,11 @@ function validateDatasourceSchemas(datasourceJsons, externalDatasourceSchema, aj
297
302
  * @throws {Error} If configuration is invalid
298
303
  */
299
304
  async function loadExternalIntegrationConfig(appPath) {
300
- const variablesPath = path.join(appPath, 'variables.yaml');
301
- const { parsed: variables } = loadVariables(variablesPath);
305
+ const configPath = resolveApplicationConfigPath(appPath);
306
+ const { parsed: variables } = loadVariables(configPath);
302
307
 
303
308
  if (!variables.externalIntegration) {
304
- throw new Error('externalIntegration block not found in variables.yaml');
309
+ throw new Error('externalIntegration block not found in application.yaml');
305
310
  }
306
311
 
307
312
  const schemaBasePath = variables.externalIntegration.schemaBasePath || './';
@@ -45,7 +45,7 @@ async function loadStepTemplates(stepNames = []) {
45
45
  /**
46
46
  * Generate GitHub Actions workflow files from templates
47
47
  * @param {string} appPath - Path to application directory
48
- * @param {Object} config - Configuration from variables.yaml
48
+ * @param {Object} config - Configuration from application.yaml
49
49
  * @param {Object} options - Generation options
50
50
  * @returns {Promise<string[]>} Array of generated file paths
51
51
  */
@@ -10,24 +10,17 @@
10
10
 
11
11
  const fs = require('fs');
12
12
  const yaml = require('js-yaml');
13
+ const { loadConfigFile } = require('../utils/config-format');
13
14
 
14
15
  /**
15
- * Loads variables.yaml file
16
- * @param {string} variablesPath - Path to variables.yaml
17
- * @returns {Object} Parsed variables
18
- * @throws {Error} If file not found or invalid YAML
16
+ * Loads application config file (application.yaml, application.json, or legacy path) via converter.
17
+ * @param {string} configPath - Path to application config file
18
+ * @returns {Object} Object with parsed config: { parsed }
19
+ * @throws {Error} If file not found or invalid YAML/JSON
19
20
  */
20
- function loadVariables(variablesPath) {
21
- if (!fs.existsSync(variablesPath)) {
22
- throw new Error(`variables.yaml not found: ${variablesPath}`);
23
- }
24
-
25
- const variablesContent = fs.readFileSync(variablesPath, 'utf8');
26
- try {
27
- return { content: variablesContent, parsed: yaml.load(variablesContent) };
28
- } catch (error) {
29
- throw new Error(`Invalid YAML syntax in variables.yaml: ${error.message}`);
30
- }
21
+ function loadVariables(configPath) {
22
+ const parsed = loadConfigFile(configPath);
23
+ return { parsed };
31
24
  }
32
25
 
33
26
  /**
@@ -151,16 +144,16 @@ function validatePortalInput(portalInput, variableName) {
151
144
  }
152
145
 
153
146
  /**
154
- * Parses environment variables from env.template and merges portalInput from variables.yaml
147
+ * Parses environment variables from env.template and merges portalInput from application config
155
148
  * @param {string} envTemplate - Content of env.template file
156
- * @param {Object|null} [variablesConfig=null] - Optional configuration from variables.yaml
149
+ * @param {Object|null} [variablesConfig=null] - Optional configuration from application.yaml
157
150
  * @returns {Array<Object>} Configuration array with merged portalInput
158
151
  * @throws {Error} If portalInput structure is invalid
159
152
  */
160
153
  /**
161
154
  * Creates a map of portalInput configurations from variables config
162
155
  * @function createPortalInputMap
163
- * @param {Object|null} variablesConfig - Configuration from variables.yaml
156
+ * @param {Object|null} variablesConfig - Configuration from application.yaml
164
157
  * @returns {Map} Map of variable names to portalInput configurations
165
158
  */
166
159
  function createPortalInputMap(variablesConfig) {
@@ -250,7 +243,7 @@ function createConfigItem(key, value, location, required, portalInputMap) {
250
243
  required
251
244
  };
252
245
 
253
- // Merge portalInput if it exists in variables.yaml
246
+ // Merge portalInput if it exists in application config
254
247
  if (portalInputMap.has(key)) {
255
248
  configItem.portalInput = portalInputMap.get(key);
256
249
  }
@@ -2,7 +2,7 @@
2
2
  * AI Fabrix Builder Deployment JSON Generator
3
3
  *
4
4
  * This module generates deployment JSON manifests for Miso Controller.
5
- * Combines variables.yaml, env.template, and rbac.yaml into deployment configuration.
5
+ * Combines application.yaml, env.template, and rbac.yaml into deployment configuration.
6
6
  *
7
7
  * @fileoverview Deployment JSON generation for AI Fabrix Builder
8
8
  * @author AI Fabrix Team
@@ -13,7 +13,8 @@ const fs = require('fs');
13
13
  const path = require('path');
14
14
  const _validator = require('../validation/validator');
15
15
  const builders = require('./builders');
16
- const { detectAppType, getDeployJsonPath } = require('../utils/paths');
16
+ const { detectAppType, getDeployJsonPath, resolveApplicationConfigPath } = require('../utils/paths');
17
+ const { logOfflinePathWhenType } = require('../utils/cli-utils');
17
18
  const splitFunctions = require('./split');
18
19
  const { loadVariables, loadEnvTemplate, loadRbac, parseEnvironmentVariables } = require('./helpers');
19
20
  const { generateExternalSystemApplicationSchema, splitExternalApplicationSchema } = require('./external');
@@ -24,13 +25,13 @@ const { resolveVersionForApp } = require('../utils/image-version');
24
25
  * Generates deployment JSON from application configuration files
25
26
  * Creates <app-name>-deploy.json for regular apps (consistent naming)
26
27
  * For external systems, generates application-schema.json
27
- * For regular apps, generates deployment manifest from variables.yaml, env.template, rbac.yaml
28
+ * For regular apps, generates deployment manifest from application.yaml, env.template, rbac.yaml
28
29
  *
29
30
  * @async
30
31
  * @function generateDeployJson
31
32
  * @param {string} appName - Name of the application
32
33
  * @param {Object} [options] - Generation options
33
- * @param {string} [options.type] - Forced application type (external)
34
+ *
34
35
  * @returns {Promise<string>} Path to generated deployment JSON file
35
36
  * @throws {Error} If generation fails or configuration is invalid
36
37
  *
@@ -46,7 +47,7 @@ const { resolveVersionForApp } = require('../utils/image-version');
46
47
  * @returns {Object} Loaded configuration files
47
48
  */
48
49
  function loadDeploymentConfigFiles(appPath, appType, appName) {
49
- const variablesPath = path.join(appPath, 'variables.yaml');
50
+ const variablesPath = resolveApplicationConfigPath(appPath);
50
51
  const templatePath = path.join(appPath, 'env.template');
51
52
  const rbacPath = path.join(appPath, 'rbac.yaml');
52
53
  const jsonPath = getDeployJsonPath(appName, appType, true); // Use new naming
@@ -69,7 +70,7 @@ function loadDeploymentConfigFiles(appPath, appType, appName) {
69
70
  * @throws {Error} If validation fails
70
71
  */
71
72
  function buildAndValidateDeployment(appName, variables, envTemplate, rbac) {
72
- // Parse environment variables from template and merge portalInput from variables.yaml
73
+ // Parse environment variables from template and merge portalInput from application config
73
74
  const configuration = parseEnvironmentVariables(envTemplate, variables);
74
75
 
75
76
  // Build deployment manifest (Controller computes deploymentKey from schema)
@@ -93,17 +94,17 @@ function buildAndValidateDeployment(appName, variables, envTemplate, rbac) {
93
94
  * @param {string} appName - Application name
94
95
  * @param {Object} [options] - Options (e.g. type for external)
95
96
  * @returns {Promise<{ deployment: Object, appPath: string }>} Manifest and app path
96
- * @throws {Error} If variables.yaml/env.template missing or generation fails
97
+ * @throws {Error} If application config/env.template missing or generation fails
97
98
  */
98
99
  async function buildDeploymentManifestInMemory(appName, options = {}) {
99
100
  if (!appName || typeof appName !== 'string') {
100
101
  throw new Error('App name is required and must be a string');
101
102
  }
102
103
 
103
- const { isExternal, appPath, appType } = await detectAppType(appName, options);
104
+ const { isExternal, appPath, appType } = await detectAppType(appName);
104
105
 
105
106
  if (isExternal) {
106
- const manifest = await generateControllerManifest(appName);
107
+ const manifest = await generateControllerManifest(appName, options);
107
108
  return { deployment: manifest, appPath };
108
109
  }
109
110
 
@@ -124,13 +125,13 @@ async function generateDeployJson(appName, options = {}) {
124
125
  throw new Error('App name is required and must be a string');
125
126
  }
126
127
 
127
- // Detect app type and get correct path (integration or builder)
128
- const { isExternal, appPath, appType } = await detectAppType(appName, options);
128
+ // Detect app type and get correct path (integration first, then builder)
129
+ const { isExternal, appPath, appType } = await detectAppType(appName);
130
+ logOfflinePathWhenType(appPath);
129
131
 
130
132
  // Check if app type is external
131
133
  if (isExternal) {
132
- // Generate controller-compatible manifest format
133
- const manifest = await generateControllerManifest(appName);
134
+ const manifest = await generateControllerManifest(appName, options);
134
135
 
135
136
  // Determine system key for file naming
136
137
  const systemKey = manifest.key || appName;
@@ -161,8 +162,7 @@ async function generateDeployJsonWithValidation(appName, options = {}) {
161
162
  const jsonContent = fs.readFileSync(jsonPath, 'utf8');
162
163
  const deployment = JSON.parse(jsonContent);
163
164
 
164
- // Detect if this is an external system
165
- const { isExternal } = await detectAppType(appName, options);
165
+ const { isExternal } = await detectAppType(appName);
166
166
 
167
167
  // For external systems, skip deployment JSON validation (they use external system JSON structure)
168
168
  if (isExternal) {
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Parses image reference string into components.
3
+ * @fileoverview Image reference parser for deployment JSON
4
+ * @author AI Fabrix Team
5
+ * @version 2.0.0
6
+ */
7
+
8
+ /**
9
+ * Parses image reference string into components
10
+ * @function parseImageReference
11
+ * @param {string} imageString - Full image string (e.g., "registry/name:tag")
12
+ * @returns {Object} Object with registry, name, and tag
13
+ */
14
+ function parseImageReference(imageString) {
15
+ if (!imageString || typeof imageString !== 'string') {
16
+ return { registry: null, name: null, tag: 'latest' };
17
+ }
18
+
19
+ const parts = imageString.split('/');
20
+ let registry = null;
21
+ let nameAndTag = imageString;
22
+
23
+ if (parts.length > 1 && parts[0].includes('.')) {
24
+ registry = parts[0];
25
+ nameAndTag = parts.slice(1).join('/');
26
+ }
27
+
28
+ const tagIndex = nameAndTag.lastIndexOf(':');
29
+ const name = tagIndex !== -1 ? nameAndTag.substring(0, tagIndex) : nameAndTag;
30
+ const tag = tagIndex !== -1 ? nameAndTag.substring(tagIndex + 1) : 'latest';
31
+
32
+ return { registry, name, tag };
33
+ }
34
+
35
+ module.exports = { parseImageReference };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * README generation from deployment JSON for split flow.
3
+ * @fileoverview Builds readme config and generates README from deployment JSON
4
+ * @author AI Fabrix Team
5
+ * @version 2.0.0
6
+ */
7
+
8
+ const { generateReadmeMd } = require('../app/readme');
9
+ const { parseImageReference } = require('./parse-image');
10
+
11
+ /**
12
+ * Builds config for external-system README from deployment
13
+ * @param {Object} deployment - Deployment JSON object
14
+ * @returns {{ appName: string, config: Object }}
15
+ */
16
+ function buildReadmeConfigForExternal(deployment) {
17
+ const system = deployment.system;
18
+ const appName = system.key || deployment.key || 'external-system';
19
+ const dataSources = deployment.dataSources || deployment.datasources || [];
20
+ return {
21
+ appName,
22
+ config: {
23
+ type: 'external',
24
+ systemKey: appName,
25
+ systemType: system.type || 'openapi',
26
+ systemDisplayName: system.displayName || appName,
27
+ systemDescription: system.description || `External system integration for ${appName}`,
28
+ datasourceCount: dataSources.length,
29
+ datasources: dataSources
30
+ }
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Builds config for application README from deployment
36
+ * @param {Object} deployment - Deployment JSON object
37
+ * @returns {{ appName: string, config: Object }}
38
+ */
39
+ function buildReadmeConfigForApp(deployment) {
40
+ const appName = deployment.key || 'application';
41
+ const imageParts = parseImageReference(deployment.image || '');
42
+ const port = deployment.port !== undefined ? deployment.port : 3000;
43
+ const imageName = imageParts.name || appName;
44
+ const registry = imageParts.registry || 'myacr.azurecr.io';
45
+
46
+ const config = {
47
+ type: deployment.type || 'webapp',
48
+ displayName: deployment.displayName,
49
+ description: deployment.description,
50
+ port,
51
+ build: { localPort: port },
52
+ image: { name: imageName, registry },
53
+ registry,
54
+ database: deployment.requiresDatabase,
55
+ requires: {
56
+ database: deployment.requiresDatabase,
57
+ redis: deployment.requiresRedis,
58
+ storage: deployment.requiresStorage
59
+ },
60
+ redis: deployment.requiresRedis,
61
+ storage: deployment.requiresStorage,
62
+ authentication: !!deployment.authentication
63
+ };
64
+
65
+ if (config.type === 'external') {
66
+ config.systemKey = appName;
67
+ config.systemType = deployment.systemType || 'openapi';
68
+ config.systemDisplayName = deployment.displayName || appName;
69
+ config.systemDescription = deployment.description || `External system integration for ${appName}`;
70
+ config.datasourceCount = 0;
71
+ config.datasources = deployment.dataSources || deployment.datasources || [];
72
+ }
73
+
74
+ return { appName, config };
75
+ }
76
+
77
+ /**
78
+ * Builds application config shape from deployment JSON for README template context.
79
+ * @param {Object} deployment - Deployment JSON object
80
+ * @returns {{ appName: string, config: Object }}
81
+ */
82
+ function buildReadmeConfigFromDeployment(deployment) {
83
+ if (deployment.system && typeof deployment.system === 'object') {
84
+ return buildReadmeConfigForExternal(deployment);
85
+ }
86
+ return buildReadmeConfigForApp(deployment);
87
+ }
88
+
89
+ /**
90
+ * Generates README.md content from deployment JSON.
91
+ * @param {Object} deployment - Deployment JSON object
92
+ * @returns {string} README.md content
93
+ */
94
+ function generateReadmeFromDeployJson(deployment) {
95
+ if (!deployment || typeof deployment !== 'object') {
96
+ throw new Error('Deployment object is required');
97
+ }
98
+ const { appName, config } = buildReadmeConfigFromDeployment(deployment);
99
+ return generateReadmeMd(appName, config);
100
+ }
101
+
102
+ module.exports = {
103
+ buildReadmeConfigFromDeployment,
104
+ generateReadmeFromDeployJson
105
+ };