@sap-ux/adp-tooling 0.18.61 → 0.18.63

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/dist/base/cf.d.ts CHANGED
@@ -4,5 +4,5 @@
4
4
  * @param {string} basePath - The path to the adaptation project.
5
5
  * @returns {boolean} true if the project is a CF project, false otherwise
6
6
  */
7
- export declare function isCFEnvironment(basePath: string): boolean;
7
+ export declare function isCFEnvironment(basePath: string): Promise<boolean>;
8
8
  //# sourceMappingURL=cf.d.ts.map
package/dist/base/cf.js CHANGED
@@ -3,13 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isCFEnvironment = isCFEnvironment;
4
4
  const node_path_1 = require("node:path");
5
5
  const node_fs_1 = require("node:fs");
6
+ const project_access_1 = require("@sap-ux/project-access");
6
7
  /**
7
8
  * Check if the project is a CF project.
8
9
  *
9
10
  * @param {string} basePath - The path to the adaptation project.
10
11
  * @returns {boolean} true if the project is a CF project, false otherwise
11
12
  */
12
- function isCFEnvironment(basePath) {
13
+ async function isCFEnvironment(basePath) {
13
14
  const configJsonPath = (0, node_path_1.join)(basePath, '.adp', 'config.json');
14
15
  if ((0, node_fs_1.existsSync)(configJsonPath)) {
15
16
  const config = JSON.parse((0, node_fs_1.readFileSync)(configJsonPath, 'utf-8'));
@@ -17,6 +18,18 @@ function isCFEnvironment(basePath) {
17
18
  return true;
18
19
  }
19
20
  }
21
+ try {
22
+ const ui5Config = await (0, project_access_1.readUi5Yaml)(basePath, 'ui5.yaml');
23
+ const customMiddleware = ui5Config.findCustomMiddleware('fiori-tools-preview') ??
24
+ ui5Config.findCustomMiddleware('preview-middleware');
25
+ const adpConfig = customMiddleware?.configuration?.adp;
26
+ if (adpConfig && 'cfBuildPath' in adpConfig && adpConfig.cfBuildPath === 'dist') {
27
+ return true;
28
+ }
29
+ }
30
+ catch {
31
+ return false;
32
+ }
20
33
  return false;
21
34
  }
22
35
  //# sourceMappingURL=cf.js.map
@@ -100,4 +100,11 @@ export declare function getWebappFiles(basePath: string): Promise<{
100
100
  * @returns {ManifestNamespace.Inbound | undefined} The transformed inbounds or undefined if input is empty.
101
101
  */
102
102
  export declare function filterAndMapInboundsToManifest(inbounds: Inbound[]): ManifestNamespace.Inbound | undefined;
103
+ /**
104
+ * Get base application ID from variant.
105
+ *
106
+ * @param basePath - path to application root
107
+ * @returns App ID (reference)
108
+ */
109
+ export declare function getBaseAppId(basePath: string): Promise<string>;
103
110
  //# sourceMappingURL=helper.d.ts.map
@@ -12,6 +12,7 @@ exports.loadAppVariant = loadAppVariant;
12
12
  exports.getAdpConfig = getAdpConfig;
13
13
  exports.getWebappFiles = getWebappFiles;
14
14
  exports.filterAndMapInboundsToManifest = filterAndMapInboundsToManifest;
15
+ exports.getBaseAppId = getBaseAppId;
15
16
  const node_fs_1 = require("node:fs");
16
17
  const node_path_1 = require("node:path");
17
18
  const project_access_1 = require("@sap-ux/project-access");
@@ -202,4 +203,22 @@ function filterAndMapInboundsToManifest(inbounds) {
202
203
  }, {});
203
204
  return Object.keys(filteredInbounds).length === 0 ? undefined : filteredInbounds;
204
205
  }
206
+ /**
207
+ * Get base application ID from variant.
208
+ *
209
+ * @param basePath - path to application root
210
+ * @returns App ID (reference)
211
+ */
212
+ async function getBaseAppId(basePath) {
213
+ try {
214
+ const variant = await getVariant(basePath);
215
+ if (!variant.reference) {
216
+ throw new Error('No reference found in manifest.appdescr_variant');
217
+ }
218
+ return variant.reference;
219
+ }
220
+ catch (error) {
221
+ throw new Error(`Failed to get app ID: ${error.message}`);
222
+ }
223
+ }
205
224
  //# sourceMappingURL=helper.js.map
@@ -4,8 +4,9 @@
4
4
  * This function uses the `CommandRunner` to run the build process via the command `npm run build`.
5
5
  *
6
6
  * @param {string} projectPath - The absolute path to the project directory where the build command will be executed.
7
+ * @param {NodeJS.ProcessEnv} [env] - Optional environment variables to be used during the build process.
7
8
  * @returns {Promise<void>} Resolves when the build process has completed successfully.
8
9
  * @throws {Error} If the build process fails or if an error occurs during cleanup.
9
10
  */
10
- export declare function runBuild(projectPath: string): Promise<void>;
11
+ export declare function runBuild(projectPath: string, env?: NodeJS.ProcessEnv): Promise<void>;
11
12
  //# sourceMappingURL=project-builder.d.ts.map
@@ -8,13 +8,18 @@ const nodejs_utils_1 = require("@sap-ux/nodejs-utils");
8
8
  * This function uses the `CommandRunner` to run the build process via the command `npm run build`.
9
9
  *
10
10
  * @param {string} projectPath - The absolute path to the project directory where the build command will be executed.
11
+ * @param {NodeJS.ProcessEnv} [env] - Optional environment variables to be used during the build process.
11
12
  * @returns {Promise<void>} Resolves when the build process has completed successfully.
12
13
  * @throws {Error} If the build process fails or if an error occurs during cleanup.
13
14
  */
14
- async function runBuild(projectPath) {
15
+ async function runBuild(projectPath, env) {
15
16
  const commandRunner = new nodejs_utils_1.CommandRunner();
16
17
  try {
17
- await commandRunner.run('npm', ['run', 'build'], { cwd: projectPath });
18
+ const opts = {
19
+ cwd: projectPath,
20
+ ...(env && { env: { ...process.env, ...env } })
21
+ };
22
+ await commandRunner.run('npm', ['run', 'build'], opts);
18
23
  }
19
24
  catch (e) {
20
25
  console.error(`Error during build and clean: ${e.message}`);
@@ -2,19 +2,42 @@ import type AdmZip from 'adm-zip';
2
2
  import type { ToolsLogger } from '@sap-ux/logger';
3
3
  import type { CfConfig, CFApp, ServiceKeys } from '../../types';
4
4
  /**
5
- * Get the app host ids.
5
+ * Get the app host ids from service keys.
6
6
  *
7
7
  * @param {ServiceKeys[]} serviceKeys - The service keys.
8
8
  * @returns {string[]} The app host ids.
9
9
  */
10
10
  export declare function getAppHostIds(serviceKeys: ServiceKeys[]): string[];
11
11
  /**
12
- * Extracts the backend URL from service key credentials. Iterates through all endpoint keys to find the first endpoint with a URL.
12
+ * Extracts all backend URLs from service key credentials. Iterates through all endpoint keys to find all endpoints with URLs.
13
+ * Handles both string endpoints and object endpoints with url property.
13
14
  *
14
15
  * @param {ServiceKeys[]} serviceKeys - The credentials from service keys.
15
- * @returns {string | undefined} The backend URL or undefined if not found.
16
+ * @returns {string[]} Array of backend URLs (including full paths) or empty array if none found.
16
17
  */
17
- export declare function getBackendUrlFromServiceKeys(serviceKeys: ServiceKeys[]): string | undefined;
18
+ export declare function getBackendUrlsFromServiceKeys(serviceKeys: ServiceKeys[]): string[];
19
+ /**
20
+ * Maps backend URLs to their corresponding OAuth paths based on destination matching
21
+ * between xs-app.json routes and credentials.json endpoints.
22
+ *
23
+ * @param {ServiceKeys[]} serviceKeys - The service keys containing endpoints with destinations.
24
+ * @param {string} basePath - Path to the .adp/reuse folder containing xs-app.json files.
25
+ * @returns {Array<{ url: string; paths: string[] }>} Array of URL-to-paths mappings.
26
+ */
27
+ export declare function getBackendUrlsWithPaths(serviceKeys: ServiceKeys[], basePath: string): Array<{
28
+ url: string;
29
+ paths: string[];
30
+ }>;
31
+ /**
32
+ * Extract endpoint destinations from service keys.
33
+ *
34
+ * @param {ServiceKeys[]} serviceKeys - The service keys.
35
+ * @returns {Array<{name: string; url: string}>} Array of endpoint destinations.
36
+ */
37
+ export declare function getServiceKeyDestinations(serviceKeys: ServiceKeys[]): Array<{
38
+ name: string;
39
+ url: string;
40
+ }>;
18
41
  /**
19
42
  * Extracts OAuth paths from xs-app.json routes that have a source property.
20
43
  * These paths should receive OAuth Bearer tokens in the middleware.
@@ -1,14 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getAppHostIds = getAppHostIds;
4
- exports.getBackendUrlFromServiceKeys = getBackendUrlFromServiceKeys;
4
+ exports.getBackendUrlsFromServiceKeys = getBackendUrlsFromServiceKeys;
5
+ exports.getBackendUrlsWithPaths = getBackendUrlsWithPaths;
6
+ exports.getServiceKeyDestinations = getServiceKeyDestinations;
5
7
  exports.getOAuthPathsFromXsApp = getOAuthPathsFromXsApp;
6
8
  exports.getCfApps = getCfApps;
9
+ const node_fs_1 = require("node:fs");
10
+ const node_path_1 = require("node:path");
7
11
  const i18n_1 = require("../../i18n");
8
12
  const utils_1 = require("../utils");
9
13
  const api_1 = require("../services/api");
10
14
  /**
11
- * Get the app host ids.
15
+ * Get the app host ids from service keys.
12
16
  *
13
17
  * @param {ServiceKeys[]} serviceKeys - The service keys.
14
18
  * @returns {string[]} The app host ids.
@@ -26,27 +30,169 @@ function getAppHostIds(serviceKeys) {
26
30
  return [...new Set(appHostIds)];
27
31
  }
28
32
  /**
29
- * Extracts the backend URL from service key credentials. Iterates through all endpoint keys to find the first endpoint with a URL.
33
+ * Extracts all backend URLs from service key credentials. Iterates through all endpoint keys to find all endpoints with URLs.
34
+ * Handles both string endpoints and object endpoints with url property.
30
35
  *
31
36
  * @param {ServiceKeys[]} serviceKeys - The credentials from service keys.
32
- * @returns {string | undefined} The backend URL or undefined if not found.
37
+ * @returns {string[]} Array of backend URLs (including full paths) or empty array if none found.
33
38
  */
34
- function getBackendUrlFromServiceKeys(serviceKeys) {
39
+ function getBackendUrlsFromServiceKeys(serviceKeys) {
35
40
  if (!serviceKeys || serviceKeys.length === 0) {
36
- return undefined;
41
+ return [];
42
+ }
43
+ const urls = [];
44
+ const endpoints = serviceKeys[0]?.credentials?.endpoints;
45
+ if (endpoints && typeof endpoints === 'object' && endpoints !== null) {
46
+ for (const key in endpoints) {
47
+ const endpoint = endpoints[key];
48
+ if (endpoint?.url) {
49
+ urls.push(endpoint.url);
50
+ }
51
+ }
37
52
  }
53
+ return urls;
54
+ }
55
+ /**
56
+ * Extract destination to URL mapping from service key endpoints.
57
+ *
58
+ * @param {ServiceKeys[]} serviceKeys - The service keys containing endpoints.
59
+ * @returns {Map<string, string>} Map of destination names to URLs.
60
+ */
61
+ function extractDestinationToUrlMap(serviceKeys) {
62
+ const destinationToUrl = new Map();
38
63
  const endpoints = serviceKeys[0]?.credentials?.endpoints;
39
- if (endpoints) {
64
+ if (endpoints && typeof endpoints === 'object') {
40
65
  for (const key in endpoints) {
41
- if (Object.hasOwn(endpoints, key)) {
42
- const endpoint = endpoints[key];
43
- if (endpoint && typeof endpoint === 'object' && endpoint.url && typeof endpoint.url === 'string') {
44
- return endpoint.url;
66
+ const endpoint = endpoints[key];
67
+ if (endpoint?.url && endpoint.destination) {
68
+ destinationToUrl.set(endpoint.destination, endpoint.url);
69
+ }
70
+ }
71
+ }
72
+ return destinationToUrl;
73
+ }
74
+ /**
75
+ * Clean regex pattern from route source.
76
+ *
77
+ * @param {string} source - The route source pattern.
78
+ * @returns {string} Cleaned path.
79
+ */
80
+ function cleanRoutePath(source) {
81
+ let path = source;
82
+ // Remove leading ^ and trailing $
83
+ path = path.replace(/^\^/, '').replace(/\$$/, '');
84
+ // Remove capture groups like (.*) or $1
85
+ path = path.replace(/\([^)]*\)/g, '');
86
+ // Remove regex quantifiers
87
+ path = path.replace(/\$\d+/g, '');
88
+ // Clean up any remaining regex characters at the end
89
+ path = path.replace(/\/?\*$/, '');
90
+ // Normalize multiple consecutive slashes to single slash
91
+ while (path.includes('//')) {
92
+ path = path.replaceAll('//', '/');
93
+ }
94
+ return path;
95
+ }
96
+ /**
97
+ * Process a route and add its cleaned path to the destination mapping.
98
+ *
99
+ * @param {XsApp['routes'][number]} route - The route object from xs-app.json.
100
+ * @param {Map<string, Set<string>>} destinationToPaths - Map to store destination to paths mapping.
101
+ */
102
+ function processRouteForDestination(route, destinationToPaths) {
103
+ const destination = route.destination;
104
+ const service = route.service;
105
+ if (!destination || service === 'html5-apps-repo-rt' || !route.source) {
106
+ return;
107
+ }
108
+ const path = cleanRoutePath(route.source);
109
+ if (path) {
110
+ if (!destinationToPaths.has(destination)) {
111
+ destinationToPaths.set(destination, new Set());
112
+ }
113
+ destinationToPaths.get(destination).add(path);
114
+ }
115
+ }
116
+ /**
117
+ * Extract destination to paths mapping from xs-app.json routes.
118
+ *
119
+ * @param {string} xsAppPath - Path to xs-app.json file.
120
+ * @returns {Map<string, Set<string>>} Map of destination names to path sets.
121
+ */
122
+ function extractDestinationToPathsMap(xsAppPath) {
123
+ const destinationToPaths = new Map();
124
+ try {
125
+ const xsAppContent = (0, node_fs_1.readFileSync)(xsAppPath, 'utf8');
126
+ const xsApp = JSON.parse(xsAppContent);
127
+ if (xsApp?.routes) {
128
+ for (const route of xsApp.routes) {
129
+ processRouteForDestination(route, destinationToPaths);
130
+ }
131
+ }
132
+ }
133
+ catch (e) {
134
+ throw new Error((0, i18n_1.t)('error.invalidXsAppJson', { error: e.message }));
135
+ }
136
+ return destinationToPaths;
137
+ }
138
+ /**
139
+ * Maps backend URLs to their corresponding OAuth paths based on destination matching
140
+ * between xs-app.json routes and credentials.json endpoints.
141
+ *
142
+ * @param {ServiceKeys[]} serviceKeys - The service keys containing endpoints with destinations.
143
+ * @param {string} basePath - Path to the .adp/reuse folder containing xs-app.json files.
144
+ * @returns {Array<{ url: string; paths: string[] }>} Array of URL-to-paths mappings.
145
+ */
146
+ function getBackendUrlsWithPaths(serviceKeys, basePath) {
147
+ const destinationToUrl = extractDestinationToUrlMap(serviceKeys);
148
+ const reuseXsAppPath = (0, node_path_1.join)(basePath, '.adp', 'reuse', 'xs-app.json');
149
+ const distXsAppPath = (0, node_path_1.join)(basePath, 'dist', 'xs-app.json');
150
+ let xsAppPath;
151
+ if ((0, node_fs_1.existsSync)(reuseXsAppPath)) {
152
+ xsAppPath = reuseXsAppPath;
153
+ }
154
+ else if ((0, node_fs_1.existsSync)(distXsAppPath)) {
155
+ xsAppPath = distXsAppPath;
156
+ }
157
+ else {
158
+ throw new Error((0, i18n_1.t)('error.xsAppJsonNotFound', { paths: `${reuseXsAppPath}, ${distXsAppPath}` }));
159
+ }
160
+ const destinationToPaths = extractDestinationToPathsMap(xsAppPath);
161
+ const result = [];
162
+ for (const [destination, paths] of destinationToPaths.entries()) {
163
+ const url = destinationToUrl.get(destination);
164
+ if (url) {
165
+ result.push({
166
+ url,
167
+ paths: Array.from(paths)
168
+ });
169
+ }
170
+ }
171
+ return result;
172
+ }
173
+ /**
174
+ * Extract endpoint destinations from service keys.
175
+ *
176
+ * @param {ServiceKeys[]} serviceKeys - The service keys.
177
+ * @returns {Array<{name: string; url: string}>} Array of endpoint destinations.
178
+ */
179
+ function getServiceKeyDestinations(serviceKeys) {
180
+ const endpointDestinations = [];
181
+ for (const key of serviceKeys) {
182
+ const endpoints = key.credentials?.endpoints;
183
+ if (endpoints && typeof endpoints === 'object') {
184
+ for (const endpointKey in endpoints) {
185
+ const endpoint = endpoints[endpointKey];
186
+ if (endpoint?.url && endpoint.destination) {
187
+ endpointDestinations.push({
188
+ name: endpoint.destination,
189
+ url: endpoint.url
190
+ });
45
191
  }
46
192
  }
47
193
  }
48
194
  }
49
- return undefined;
195
+ return endpointDestinations;
50
196
  }
51
197
  /**
52
198
  * Extracts OAuth paths from xs-app.json routes that have a source property.
@@ -65,19 +211,7 @@ function getOAuthPathsFromXsApp(zipEntries) {
65
211
  if (route.service === 'html5-apps-repo-rt' || !route.source) {
66
212
  continue;
67
213
  }
68
- let path = route.source;
69
- // Remove leading ^ and trailing $
70
- path = path.replace(/^\^/, '').replace(/\$$/, '');
71
- // Remove capture groups like (.*) or $1
72
- path = path.replace(/\([^)]*\)/g, '');
73
- // Remove regex quantifiers
74
- path = path.replace(/\$\d+/g, '');
75
- // Clean up any remaining regex characters at the end
76
- path = path.replace(/\/?\*$/, '');
77
- // Normalize multiple consecutive slashes to single slash
78
- while (path.includes('//')) {
79
- path = path.replaceAll('//', '/');
80
- }
214
+ const path = cleanRoutePath(route.source);
81
215
  if (path) {
82
216
  pathsSet.add(path);
83
217
  }
@@ -6,5 +6,5 @@ import type { CfConfig } from '../../types';
6
6
  * @param {ToolsLogger} logger - The logger.
7
7
  * @returns {CfConfig} The CF configuration.
8
8
  */
9
- export declare function loadCfConfig(logger: ToolsLogger): CfConfig;
9
+ export declare function loadCfConfig(logger?: ToolsLogger): CfConfig;
10
10
  //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1,14 @@
1
+ import type { ToolsLogger } from '@sap-ux/logger';
2
+ /**
3
+ * Extracts reusable library paths from ui5AppInfo.json if it exists.
4
+ *
5
+ * @param basePath - path to application root
6
+ * @param logger - logger instance
7
+ * @returns Array of path configurations for reusable libraries
8
+ */
9
+ export declare function getReusableLibraryPaths(basePath: string, logger?: ToolsLogger): Array<{
10
+ path: string;
11
+ src: string;
12
+ fallthrough: boolean;
13
+ }>;
14
+ //# sourceMappingURL=ui5-app-info.d.ts.map
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getReusableLibraryPaths = getReusableLibraryPaths;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ /**
10
+ * Extracts reusable library paths from ui5AppInfo.json if it exists.
11
+ *
12
+ * @param basePath - path to application root
13
+ * @param logger - logger instance
14
+ * @returns Array of path configurations for reusable libraries
15
+ */
16
+ function getReusableLibraryPaths(basePath, logger) {
17
+ const ui5AppInfoPath = node_path_1.default.join(basePath, 'ui5AppInfo.json');
18
+ if (!node_fs_1.default.existsSync(ui5AppInfoPath)) {
19
+ logger?.warn('ui5AppInfo.json not found in project root');
20
+ return [];
21
+ }
22
+ const ui5AppInfoData = JSON.parse(node_fs_1.default.readFileSync(ui5AppInfoPath, 'utf-8'));
23
+ const ui5AppInfo = ui5AppInfoData[Object.keys(ui5AppInfoData)[0]];
24
+ const reusableLibs = ui5AppInfo.asyncHints?.libs?.filter((lib) => lib.html5AppName && lib.url && typeof lib.url === 'object' && lib.url.url !== undefined) ?? [];
25
+ return reusableLibs.map((lib) => {
26
+ const libName = String(lib.name);
27
+ const html5AppName = String(lib.html5AppName);
28
+ const resourcePath = '/resources/' + libName.replaceAll('.', '/');
29
+ return {
30
+ path: resourcePath,
31
+ src: `./.adp/reuse/${html5AppName}`,
32
+ fallthrough: false
33
+ };
34
+ });
35
+ }
36
+ //# sourceMappingURL=ui5-app-info.js.map
@@ -1,6 +1,7 @@
1
1
  import type { Editor } from 'mem-fs-editor';
2
2
  import type { ToolsLogger } from '@sap-ux/logger';
3
- import type { AppParamsExtended, MtaYaml } from '../../types';
3
+ import type { UI5Config } from '@sap-ux/ui5-config';
4
+ import type { AppParamsExtended, MtaYaml, ServiceKeys } from '../../types';
4
5
  import { AppRouterType } from '../../types';
5
6
  interface AdjustMtaYamlParams {
6
7
  projectPath: string;
@@ -8,6 +9,7 @@ interface AdjustMtaYamlParams {
8
9
  appRouterType: AppRouterType;
9
10
  businessSolutionName: string;
10
11
  businessService: string;
12
+ serviceKeys?: ServiceKeys[];
11
13
  }
12
14
  /**
13
15
  * Checks if the selected path is a MTA project.
@@ -46,6 +48,23 @@ export declare function getAppParamsFromUI5Yaml(projectPath: string): AppParamsE
46
48
  * @param {ToolsLogger} logger - The logger.
47
49
  * @returns {Promise<void>} The promise.
48
50
  */
49
- export declare function adjustMtaYaml({ projectPath, adpProjectName, appRouterType, businessSolutionName, businessService }: AdjustMtaYamlParams, memFs: Editor, templatePathOverwrite?: string, logger?: ToolsLogger): Promise<void>;
51
+ export declare function adjustMtaYaml({ projectPath, adpProjectName, appRouterType, businessSolutionName, businessService, serviceKeys }: AdjustMtaYamlParams, memFs: Editor, templatePathOverwrite?: string, logger?: ToolsLogger): Promise<void>;
52
+ /**
53
+ * Add fiori-tools-servestatic configuration to ui5.yaml and removes previously added configuration.
54
+ *
55
+ * @param basePath - path to application root
56
+ * @param ui5Config - UI5 configuration object
57
+ * @param logger - logger instance
58
+ */
59
+ export declare function addServeStaticMiddleware(basePath: string, ui5Config: UI5Config, logger?: ToolsLogger): Promise<void>;
60
+ /**
61
+ * Add backend-proxy-middleware-cf configuration to ui5.yaml.
62
+ *
63
+ * @param basePath - path to application root
64
+ * @param ui5Config - UI5 configuration object
65
+ * @param serviceKeys - service keys from Cloud Foundry
66
+ * @param logger - logger instance
67
+ */
68
+ export declare function addBackendProxyMiddleware(basePath: string, ui5Config: UI5Config, serviceKeys: ServiceKeys[], logger?: ToolsLogger): void;
50
69
  export {};
51
70
  //# sourceMappingURL=yaml.d.ts.map
@@ -41,12 +41,17 @@ exports.getSAPCloudService = getSAPCloudService;
41
41
  exports.getRouterType = getRouterType;
42
42
  exports.getAppParamsFromUI5Yaml = getAppParamsFromUI5Yaml;
43
43
  exports.adjustMtaYaml = adjustMtaYaml;
44
+ exports.addServeStaticMiddleware = addServeStaticMiddleware;
45
+ exports.addBackendProxyMiddleware = addBackendProxyMiddleware;
44
46
  const node_fs_1 = __importDefault(require("node:fs"));
45
47
  const path = __importStar(require("node:path"));
46
48
  const js_yaml_1 = __importDefault(require("js-yaml"));
47
49
  const types_1 = require("../../types");
48
50
  const api_1 = require("../services/api");
49
51
  const yaml_loader_1 = require("./yaml-loader");
52
+ const discovery_1 = require("../app/discovery");
53
+ const helper_1 = require("../../base/helper");
54
+ const ui5_app_info_1 = require("./ui5-app-info");
50
55
  const CF_MANAGED_SERVICE = 'org.cloudfoundry.managed-service';
51
56
  const HTML5_APPS_REPO = 'html5-apps-repo';
52
57
  const SAP_APPLICATION_CONTENT = 'com.sap.application.content';
@@ -148,12 +153,22 @@ function adjustMtaYamlStandaloneApprouter(yamlContent, projectName, businessServ
148
153
  * @param {string} businessSolution - The business solution.
149
154
  * @param {string} businessService - The business service.
150
155
  * @param {string} timestamp - The timestamp.
156
+ * @param {ServiceKeys[]} serviceKeys - The service keys (optional).
151
157
  */
152
- function adjustMtaYamlManagedApprouter(yamlContent, projectName, businessSolution, businessService, timestamp) {
158
+ function adjustMtaYamlManagedApprouter(yamlContent, projectName, businessSolution, businessService, timestamp, serviceKeys) {
153
159
  const projectNameForXsSecurity = (0, yaml_loader_1.getProjectNameForXsSecurity)(yamlContent, timestamp);
154
160
  const appRouterName = `${projectName}-destination-content`;
155
161
  let appRouter = yamlContent.modules?.find((module) => module.name === appRouterName);
156
162
  if (appRouter == null) {
163
+ const endpointDestinations = serviceKeys
164
+ ? (0, discovery_1.getServiceKeyDestinations)(serviceKeys).map((endpoint) => ({
165
+ Name: endpoint.name,
166
+ URL: endpoint.url,
167
+ Authentication: 'OAuth2UserTokenExchange',
168
+ ServiceInstanceName: businessService,
169
+ ServiceKeyName: `${businessService}-key`
170
+ }))
171
+ : [];
157
172
  businessSolution = businessSolution.split('.').join('_');
158
173
  appRouter = {
159
174
  name: appRouterName,
@@ -215,7 +230,9 @@ function adjustMtaYamlManagedApprouter(yamlContent, projectName, businessSolutio
215
230
  Authentication: 'OAuth2UserTokenExchange',
216
231
  ServiceInstanceName: `${businessService}`,
217
232
  ServiceKeyName: `${businessService}-key`
218
- }
233
+ },
234
+ // Add endpoint destinations from service keys
235
+ ...endpointDestinations
219
236
  ],
220
237
  existing_destinations_policy: 'update'
221
238
  }
@@ -400,7 +417,7 @@ function adjustMtaYamlFlpModule(yamlContent, projectName, businessService) {
400
417
  * @param {ToolsLogger} logger - The logger.
401
418
  * @returns {Promise<void>} The promise.
402
419
  */
403
- async function adjustMtaYaml({ projectPath, adpProjectName, appRouterType, businessSolutionName, businessService }, memFs, templatePathOverwrite, logger) {
420
+ async function adjustMtaYaml({ projectPath, adpProjectName, appRouterType, businessSolutionName, businessService, serviceKeys }, memFs, templatePathOverwrite, logger) {
404
421
  const timestamp = Date.now().toString();
405
422
  const mtaYamlPath = path.join(projectPath, 'mta.yaml');
406
423
  const loadedYamlContent = (0, yaml_loader_1.getYamlContent)(mtaYamlPath);
@@ -422,7 +439,7 @@ async function adjustMtaYaml({ projectPath, adpProjectName, appRouterType, busin
422
439
  adjustMtaYamlStandaloneApprouter(yamlContent, mtaProjectName, businessService);
423
440
  }
424
441
  else {
425
- adjustMtaYamlManagedApprouter(yamlContent, mtaProjectName, businessSolutionName, businessService, timestamp);
442
+ adjustMtaYamlManagedApprouter(yamlContent, mtaProjectName, businessSolutionName, businessService, timestamp, serviceKeys);
426
443
  }
427
444
  adjustMtaYamlUDeployer(yamlContent, mtaProjectName, adpProjectName);
428
445
  adjustMtaYamlResources(yamlContent, mtaProjectName, timestamp, !isStandaloneApprouter);
@@ -430,8 +447,78 @@ async function adjustMtaYaml({ projectPath, adpProjectName, appRouterType, busin
430
447
  // should go last since it sorts the modules (workaround, should be removed after fixed in deployment module)
431
448
  adjustMtaYamlFlpModule(yamlContent, mtaProjectName, businessService);
432
449
  await (0, api_1.createServices)(yamlContent, initialServices, timestamp, templatePathOverwrite, logger);
433
- const updatedYamlContent = js_yaml_1.default.dump(yamlContent);
450
+ const updatedYamlContent = js_yaml_1.default.dump(yamlContent, {
451
+ lineWidth: -1 // Disable line wrapping to keep URLs on single lines
452
+ });
434
453
  memFs.write(mtaYamlPath, updatedYamlContent);
435
454
  logger?.debug(`Adjusted MTA YAML for project ${projectPath}`);
436
455
  }
456
+ /**
457
+ * Add fiori-tools-servestatic configuration to ui5.yaml and removes previously added configuration.
458
+ *
459
+ * @param basePath - path to application root
460
+ * @param ui5Config - UI5 configuration object
461
+ * @param logger - logger instance
462
+ */
463
+ async function addServeStaticMiddleware(basePath, ui5Config, logger) {
464
+ try {
465
+ if (ui5Config.findCustomMiddleware('fiori-tools-servestatic')) {
466
+ ui5Config.removeCustomMiddleware('fiori-tools-servestatic');
467
+ }
468
+ const paths = [];
469
+ // Add reusable library paths from ui5AppInfo.json if it exists
470
+ paths.push(...(0, ui5_app_info_1.getReusableLibraryPaths)(basePath, logger));
471
+ const variant = await (0, helper_1.getVariant)(basePath);
472
+ paths.push({
473
+ path: `/changes/${variant.id.replaceAll('.', '_')}`,
474
+ src: './webapp/changes',
475
+ fallthrough: true
476
+ });
477
+ ui5Config.addCustomMiddleware([
478
+ {
479
+ name: 'fiori-tools-servestatic',
480
+ beforeMiddleware: 'compression',
481
+ configuration: {
482
+ paths
483
+ }
484
+ }
485
+ ]);
486
+ }
487
+ catch (error) {
488
+ logger?.warn(`Could not add fiori-tools-servestatic configuration: ${error.message}`);
489
+ throw error;
490
+ }
491
+ }
492
+ /**
493
+ * Add backend-proxy-middleware-cf configuration to ui5.yaml.
494
+ *
495
+ * @param basePath - path to application root
496
+ * @param ui5Config - UI5 configuration object
497
+ * @param serviceKeys - service keys from Cloud Foundry
498
+ * @param logger - logger instance
499
+ */
500
+ function addBackendProxyMiddleware(basePath, ui5Config, serviceKeys, logger) {
501
+ try {
502
+ if (ui5Config.findCustomMiddleware('backend-proxy-middleware-cf')) {
503
+ ui5Config.removeCustomMiddleware('backend-proxy-middleware-cf');
504
+ }
505
+ const urlsWithPaths = (0, discovery_1.getBackendUrlsWithPaths)(serviceKeys, basePath);
506
+ if (urlsWithPaths.length === 0) {
507
+ logger?.info('No backend URLs with paths found. Skipping backend-proxy-middleware-cf configuration.');
508
+ return;
509
+ }
510
+ ui5Config.addCustomMiddleware([
511
+ {
512
+ name: 'backend-proxy-middleware-cf',
513
+ afterMiddleware: 'compression',
514
+ configuration: {
515
+ backends: urlsWithPaths
516
+ }
517
+ }
518
+ ]);
519
+ }
520
+ catch (error) {
521
+ logger?.warn(`Could not add backend-proxy-middleware-cf configuration: ${error.message}`);
522
+ }
523
+ }
437
524
  //# sourceMappingURL=yaml.js.map
@@ -1,5 +1,5 @@
1
1
  import type { ToolsLogger } from '@sap-ux/logger';
2
- import type { CfConfig, CFApp, RequestArguments, ServiceKeys, GetServiceInstanceParams, ServiceInstance, MtaYaml, ServiceInfo } from '../../types';
2
+ import type { CfConfig, CFApp, RequestArguments, ServiceKeys, GetServiceInstanceParams, ServiceInstance, MtaYaml, ServiceInfo, CfUi5AppInfo } from '../../types';
3
3
  interface CreateServiceOptions {
4
4
  xsSecurityProjectName?: string;
5
5
  templatePathOverwrite?: string;
@@ -30,6 +30,16 @@ export declare function getFDCRequestArguments(cfConfig: CfConfig): RequestArgum
30
30
  * @returns {Promise<CFApp[]>} The FDC apps.
31
31
  */
32
32
  export declare function getFDCApps(appHostIds: string[], cfConfig: CfConfig, logger: ToolsLogger): Promise<CFApp[]>;
33
+ /**
34
+ * Get ui5AppInfo.json from FDC service.
35
+ *
36
+ * @param {string} appId - The application ID.
37
+ * @param {string[]} appHostIds - The app host IDs.
38
+ * @param {CfConfig} cfConfig - The CF config.
39
+ * @param {ToolsLogger} logger - The logger.
40
+ * @returns {Promise<CfUi5AppInfo>} The ui5AppInfo.json content.
41
+ */
42
+ export declare function getCfUi5AppInfo(appId: string, appHostIds: string[], cfConfig: CfConfig, logger?: ToolsLogger): Promise<CfUi5AppInfo>;
33
43
  /**
34
44
  * Creates a service instance.
35
45
  *
@@ -66,7 +76,7 @@ export declare function createServices(yamlContent: MtaYaml, initialServices: st
66
76
  * @param {ToolsLogger} logger - The logger.
67
77
  * @returns {Promise<ServiceInfo | null>} The service instance keys.
68
78
  */
69
- export declare function getServiceInstanceKeys(serviceInstanceQuery: GetServiceInstanceParams, logger: ToolsLogger): Promise<ServiceInfo | null>;
79
+ export declare function getServiceInstanceKeys(serviceInstanceQuery: GetServiceInstanceParams, logger?: ToolsLogger): Promise<ServiceInfo | null>;
70
80
  /**
71
81
  * Gets the service instance keys.
72
82
  *
@@ -74,6 +84,6 @@ export declare function getServiceInstanceKeys(serviceInstanceQuery: GetServiceI
74
84
  * @param {ToolsLogger} logger - The logger.
75
85
  * @returns {Promise<ServiceKeys[]>} The service instance keys.
76
86
  */
77
- export declare function getOrCreateServiceKeys(serviceInstance: ServiceInstance, logger: ToolsLogger): Promise<ServiceKeys[]>;
87
+ export declare function getOrCreateServiceKeys(serviceInstance: ServiceInstance, logger?: ToolsLogger): Promise<ServiceKeys[]>;
78
88
  export {};
79
89
  //# sourceMappingURL=api.d.ts.map
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.getBusinessServiceInfo = getBusinessServiceInfo;
40
40
  exports.getFDCRequestArguments = getFDCRequestArguments;
41
41
  exports.getFDCApps = getFDCApps;
42
+ exports.getCfUi5AppInfo = getCfUi5AppInfo;
42
43
  exports.createServiceInstance = createServiceInstance;
43
44
  exports.getServiceNameByTags = getServiceNameByTags;
44
45
  exports.createServices = createServices;
@@ -90,10 +91,17 @@ function getFDCRequestArguments(cfConfig) {
90
91
  }
91
92
  };
92
93
  let url;
94
+ const isBAS = (0, btp_utils_1.isAppStudio)();
93
95
  if (endpointParts?.[3]) {
94
- // Public cloud - use mTLS enabled domain with "cert" prefix
96
+ // Public cloud
95
97
  const region = endpointParts[1];
96
- url = `${fdcUrl}cert.cfapps.${region}.hana.ondemand.com`;
98
+ // Use mTLS enabled domain with "cert" prefix only in BAS, otherwise use regular domain
99
+ if (isBAS) {
100
+ url = `${fdcUrl}cert.cfapps.${region}.hana.ondemand.com`;
101
+ }
102
+ else {
103
+ url = `${fdcUrl}cfapps.${region}.hana.ondemand.com`;
104
+ }
97
105
  }
98
106
  else if (endpointParts?.[4]?.endsWith('.cn')) {
99
107
  // China has a special URL pattern
@@ -104,9 +112,7 @@ function getFDCRequestArguments(cfConfig) {
104
112
  else {
105
113
  url = `${fdcUrl}sapui5flex.cfapps${endpointParts?.[4]}`;
106
114
  }
107
- // Add authorization token for non-BAS environments or private cloud
108
- // For BAS environments with mTLS, the certificate authentication is handled automatically
109
- if (!(0, btp_utils_1.isAppStudio)() || !endpointParts?.[3]) {
115
+ if (!isBAS || !endpointParts?.[3]) {
110
116
  options.headers['Authorization'] = `Bearer ${cfConfig.token}`;
111
117
  }
112
118
  return {
@@ -142,6 +148,35 @@ async function getFDCApps(appHostIds, cfConfig, logger) {
142
148
  throw new Error((0, i18n_1.t)('error.failedToGetFDCApps', { error: error.message }));
143
149
  }
144
150
  }
151
+ /**
152
+ * Get ui5AppInfo.json from FDC service.
153
+ *
154
+ * @param {string} appId - The application ID.
155
+ * @param {string[]} appHostIds - The app host IDs.
156
+ * @param {CfConfig} cfConfig - The CF config.
157
+ * @param {ToolsLogger} logger - The logger.
158
+ * @returns {Promise<CfUi5AppInfo>} The ui5AppInfo.json content.
159
+ */
160
+ async function getCfUi5AppInfo(appId, appHostIds, cfConfig, logger) {
161
+ const requestArguments = getFDCRequestArguments(cfConfig);
162
+ const appHostIdParams = appHostIds.map((id) => `appHostId=${encodeURIComponent(id)}`).join('&');
163
+ const url = `${requestArguments.url}/api/business-service/ui5appinfo?appId=${encodeURIComponent(appId)}&${appHostIdParams}`;
164
+ logger?.log(`Fetching ui5AppInfo.json from FDC: ${url}`);
165
+ try {
166
+ const response = await axios_1.default.get(url, requestArguments.options);
167
+ if (response.status === 200) {
168
+ logger?.log('Successfully retrieved ui5AppInfo.json from FDC');
169
+ return response.data;
170
+ }
171
+ else {
172
+ throw new Error((0, i18n_1.t)('error.failedToConnectToFDCService', { status: response.status }));
173
+ }
174
+ }
175
+ catch (error) {
176
+ logger?.error(`Getting ui5AppInfo.json failed. Request url: ${url}. ${error}`);
177
+ throw new Error(`Failed to get ui5AppInfo.json from FDC: ${error.message}`);
178
+ }
179
+ }
145
180
  /**
146
181
  * Creates a service instance.
147
182
  *
@@ -74,6 +74,10 @@ async function requestCfApi(url) {
74
74
  try {
75
75
  const response = await cf_tools_1.Cli.execute(['curl', url], ENV);
76
76
  if (response.exitCode === 0) {
77
+ // Check for empty response which typically indicates authentication issues
78
+ if (!response.stdout || response.stdout.trim() === '') {
79
+ throw new Error((0, i18n_1.t)('error.emptyCFAPIResponse'));
80
+ }
77
81
  try {
78
82
  return JSON.parse(response.stdout);
79
83
  }
@@ -8,5 +8,5 @@ import { type NewModelAnswers } from '../../types';
8
8
  * @param {UI5FlexLayer} layer - UI5 Flex layer.
9
9
  * @returns {YUIQuestion<NewModelAnswers>[]} The questions/prompts.
10
10
  */
11
- export declare function getPrompts(projectPath: string, layer: UI5FlexLayer): YUIQuestion<NewModelAnswers>[];
11
+ export declare function getPrompts(projectPath: string, layer: UI5FlexLayer): Promise<YUIQuestion<NewModelAnswers>[]>;
12
12
  //# sourceMappingURL=index.d.ts.map
@@ -163,10 +163,10 @@ function validatePromptURI(value) {
163
163
  * @param {UI5FlexLayer} layer - UI5 Flex layer.
164
164
  * @returns {YUIQuestion<NewModelAnswers>[]} The questions/prompts.
165
165
  */
166
- function getPrompts(projectPath, layer) {
166
+ async function getPrompts(projectPath, layer) {
167
167
  const isCustomerBase = "CUSTOMER_BASE" /* FlexLayer.CUSTOMER_BASE */ === layer;
168
168
  const defaultSeviceName = isCustomerBase ? "customer." /* NamespacePrefix.CUSTOMER */ : "" /* NamespacePrefix.EMPTY */;
169
- const isCFEnv = (0, cf_1.isCFEnvironment)(projectPath);
169
+ const isCFEnv = await (0, cf_1.isCFEnvironment)(projectPath);
170
170
  const changeFiles = (0, change_utils_1.getChangesByType)(projectPath, "appdescr_ui5_addNewModel" /* ChangeType.ADD_NEW_MODEL */, 'manifest');
171
171
  return [
172
172
  {
@@ -100,11 +100,17 @@
100
100
  "failedToGetCredentialsFromHtml5Repo": "Failed to retrieve the credentials from the HTML5 repository for space: {{spaceGuid}}. Error: {{error}}",
101
101
  "failedToParseCFAPIResponse": "Failed to parse the CF API response: {{error}}",
102
102
  "failedToRequestCFAPI": "Request to the CF API failed. Error: {{error}}",
103
+ "emptyCFAPIResponse": "Empty response from CF API. Please verify your CF login status with 'cf target' and re-authenticate if needed with 'cf login'.",
103
104
  "xsSecurityJsonCouldNotBeParsed": "The xs-security.json file could not be parsed. Ensure the xs-security.json file exists and try again.",
104
105
  "failedToCreateServiceInstance": "Failed to create the service instance: '{{serviceInstanceName}}'. Error: {{error}}",
105
106
  "failedToGetFDCApps": "Retrieving FDC apps failed: {{error}}",
106
107
  "failedToConnectToFDCService": "Failed to connect to the FDC service: '{{status}}'",
107
- "baseAppRequired": "A base app is required for CF project generation. Please select a base app and try again."
108
+ "baseAppRequired": "A base app is required for Cloud Foundry project generation. Please select a base app and try again.",
109
+ "invalidXsAppJson": "The xs-app.json file is invalid: {{error}}",
110
+ "xsAppJsonNotFound": "The xs-app.json file was not found in any of the expected locations: {{paths}}",
111
+ "noServiceInstanceNameFound": "No serviceInstanceName found in the app-variant-bundler-build configuration",
112
+ "noServiceKeysFoundForInstance": "No service keys found for service instance: {{serviceInstanceName}}",
113
+ "couldNotFetchServiceKeys": "Could not fetch the service keys: {{error}}"
108
114
  },
109
115
  "choices": {
110
116
  "true": "true",
package/dist/types.d.ts CHANGED
@@ -798,6 +798,17 @@ export interface BusinessServiceResource {
798
798
  name: string;
799
799
  label: string;
800
800
  }
801
+ export interface CfUi5AppInfo {
802
+ asyncHints?: {
803
+ libs?: Array<{
804
+ name: string;
805
+ html5AppName?: string;
806
+ url?: {
807
+ url: string;
808
+ };
809
+ }>;
810
+ };
811
+ }
801
812
  /**
802
813
  * Cloud Foundry ADP UI5 YAML Types
803
814
  */
@@ -953,13 +964,17 @@ export interface CfAdpWriterConfig {
953
964
  */
954
965
  serviceInstanceGuid?: string;
955
966
  /**
956
- * Backend URL from service instance keys.
967
+ * Backend URLs from service instance keys.
957
968
  */
958
- backendUrl?: string;
969
+ backendUrls?: string[];
959
970
  /**
960
971
  * OAuth paths extracted from xs-app.json routes that have a source property.
961
972
  */
962
973
  oauthPaths?: string[];
974
+ /**
975
+ * Business service instance keys.
976
+ */
977
+ serviceInfo?: ServiceInfo | null;
963
978
  };
964
979
  project: {
965
980
  name: string;
@@ -990,13 +1005,14 @@ export interface CreateCfConfigParams {
990
1005
  manifest: Manifest;
991
1006
  html5RepoRuntimeGuid: string;
992
1007
  serviceInstanceGuid?: string;
993
- backendUrl?: string;
1008
+ backendUrls?: string[];
994
1009
  oauthPaths?: string[];
995
1010
  projectPath: string;
996
1011
  addStandaloneApprouter?: boolean;
997
1012
  publicVersions: UI5Version;
998
1013
  packageJson: Package;
999
1014
  toolsId: string;
1015
+ serviceInfo?: ServiceInfo | null;
1000
1016
  }
1001
1017
  export declare const AppRouterType: {
1002
1018
  readonly MANAGED: "Managed HTML5 Application Runtime";
@@ -1,6 +1,6 @@
1
1
  import { type Editor } from 'mem-fs-editor';
2
2
  import { type ToolsLogger } from '@sap-ux/logger';
3
- import type { CfAdpWriterConfig } from '../types';
3
+ import type { CfAdpWriterConfig, CfUi5AppInfo, CfConfig } from '../types';
4
4
  /**
5
5
  * Writes the CF adp-project template to the mem-fs-editor instance.
6
6
  *
@@ -11,4 +11,23 @@ import type { CfAdpWriterConfig } from '../types';
11
11
  * @returns {Promise<Editor>} The updated memfs editor instance.
12
12
  */
13
13
  export declare function generateCf(basePath: string, config: CfAdpWriterConfig, logger?: ToolsLogger, fs?: Editor): Promise<Editor>;
14
+ /**
15
+ * Fetch ui5AppInfo.json and write it to the project root.
16
+ *
17
+ * @param basePath - path to application root
18
+ * @param ui5AppInfo - ui5AppInfo.json content
19
+ * @param logger - logger instance
20
+ */
21
+ export declare function writeUi5AppInfo(basePath: string, ui5AppInfo: CfUi5AppInfo, logger?: ToolsLogger): Promise<void>;
22
+ /**
23
+ * Generate CF configuration for an adaptation project.
24
+ *
25
+ * @param basePath - path to project root
26
+ * @param yamlPath - path to the project configuration file in YAML format
27
+ * @param cfConfig - CF configuration
28
+ * @param logger - logger instance
29
+ * @param fs - mem-fs editor instance
30
+ * @returns updated mem-fs editor instance
31
+ */
32
+ export declare function generateCfConfig(basePath: string, yamlPath: string, cfConfig: CfConfig, logger?: ToolsLogger, fs?: Editor): Promise<Editor>;
14
33
  //# sourceMappingURL=cf.d.ts.map
package/dist/writer/cf.js CHANGED
@@ -1,13 +1,23 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.generateCf = generateCf;
7
+ exports.writeUi5AppInfo = writeUi5AppInfo;
8
+ exports.generateCfConfig = generateCfConfig;
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const node_path_1 = require("node:path");
4
11
  const mem_fs_1 = require("mem-fs");
5
12
  const mem_fs_editor_1 = require("mem-fs-editor");
13
+ const project_access_1 = require("@sap-ux/project-access");
6
14
  const cf_1 = require("../cf");
7
15
  const source_1 = require("../source");
8
16
  const manifest_1 = require("./manifest");
9
17
  const project_utils_1 = require("./project-utils");
10
18
  const i18n_1 = require("./i18n");
19
+ const helper_1 = require("../base/helper");
20
+ const project_builder_1 = require("../base/project-builder");
11
21
  /**
12
22
  * Writes the CF adp-project template to the mem-fs-editor instance.
13
23
  *
@@ -28,7 +38,8 @@ async function generateCf(basePath, config, logger, fs) {
28
38
  adpProjectName: project.name,
29
39
  appRouterType: cf.approuter,
30
40
  businessSolutionName: cf.businessSolutionName ?? '',
31
- businessService: cf.businessService
41
+ businessService: cf.businessService,
42
+ serviceKeys: cf.serviceInfo?.serviceKeys
32
43
  }, fs, config.options?.templatePathOverwrite, logger);
33
44
  if (fullConfig.app.i18nModels) {
34
45
  (0, i18n_1.writeI18nModels)(basePath, fullConfig.app.i18nModels, fs);
@@ -62,4 +73,59 @@ function setDefaults(config) {
62
73
  };
63
74
  return configWithDefaults;
64
75
  }
76
+ /**
77
+ * Fetch ui5AppInfo.json and write it to the project root.
78
+ *
79
+ * @param basePath - path to application root
80
+ * @param ui5AppInfo - ui5AppInfo.json content
81
+ * @param logger - logger instance
82
+ */
83
+ async function writeUi5AppInfo(basePath, ui5AppInfo, logger) {
84
+ try {
85
+ const ui5AppInfoTargetPath = (0, node_path_1.join)(basePath, 'ui5AppInfo.json');
86
+ node_fs_1.default.writeFileSync(ui5AppInfoTargetPath, JSON.stringify(ui5AppInfo, null, 2), 'utf-8');
87
+ logger?.info(`Written ui5AppInfo.json to ${basePath}`);
88
+ }
89
+ catch (error) {
90
+ logger?.error(`Failed to process ui5AppInfo.json: ${error.message}`);
91
+ throw error;
92
+ }
93
+ }
94
+ /**
95
+ * Generate CF configuration for an adaptation project.
96
+ *
97
+ * @param basePath - path to project root
98
+ * @param yamlPath - path to the project configuration file in YAML format
99
+ * @param cfConfig - CF configuration
100
+ * @param logger - logger instance
101
+ * @param fs - mem-fs editor instance
102
+ * @returns updated mem-fs editor instance
103
+ */
104
+ async function generateCfConfig(basePath, yamlPath, cfConfig, logger, fs) {
105
+ fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
106
+ const ui5Config = await (0, project_access_1.readUi5Yaml)(basePath, yamlPath);
107
+ const bundlerTask = ui5Config.findCustomTask('app-variant-bundler-build');
108
+ const serviceInstanceName = bundlerTask?.configuration?.serviceInstanceName;
109
+ if (!serviceInstanceName) {
110
+ throw new Error('No serviceInstanceName found in app-variant-bundler-build configuration');
111
+ }
112
+ const serviceInfo = await (0, cf_1.getServiceInstanceKeys)({
113
+ names: [serviceInstanceName]
114
+ }, logger);
115
+ if (!serviceInfo || serviceInfo.serviceKeys.length === 0) {
116
+ throw new Error(`No service keys found for service instance: ${serviceInstanceName}`);
117
+ }
118
+ const appId = await (0, helper_1.getBaseAppId)(basePath);
119
+ const appHostIds = (0, cf_1.getAppHostIds)(serviceInfo.serviceKeys);
120
+ const ui5AppInfo = await (0, cf_1.getCfUi5AppInfo)(appId, appHostIds, cfConfig, logger);
121
+ if (appHostIds.length === 0) {
122
+ throw new Error('No app host IDs found in service keys.');
123
+ }
124
+ await writeUi5AppInfo(basePath, ui5AppInfo, logger);
125
+ await (0, cf_1.addServeStaticMiddleware)(basePath, ui5Config, logger);
126
+ await (0, project_builder_1.runBuild)(basePath, { ADP_BUILDER_MODE: 'preview' });
127
+ (0, cf_1.addBackendProxyMiddleware)(basePath, ui5Config, serviceInfo.serviceKeys, logger);
128
+ fs.write((0, node_path_1.join)(basePath, yamlPath), ui5Config.toString());
129
+ return fs;
130
+ }
65
131
  //# sourceMappingURL=cf.js.map
@@ -74,6 +74,11 @@ export declare function enhanceUI5YamlWithCfCustomTask(ui5Config: UI5Config, con
74
74
  * @param {UI5Config} ui5Config - Configuration representing the ui5.yaml.
75
75
  * @param {CfAdpWriterConfig} config - Full project configuration.
76
76
  */
77
- export declare function enhanceUI5YamlWithCfCustomMiddleware(ui5Config: UI5Config, config: CfAdpWriterConfig): void;
77
+ /**
78
+ * Generate custom middleware configuration (fiori-tools-proxy and fiori-tools-preview only).
79
+ *
80
+ * @param {UI5Config} ui5Config - Configuration representing the ui5.yaml.
81
+ */
82
+ export declare function enhanceUI5YamlWithFioriToolsMiddleware(ui5Config: UI5Config): void;
78
83
  export {};
79
84
  //# sourceMappingURL=options.d.ts.map
@@ -8,7 +8,7 @@ exports.hasDeployConfig = hasDeployConfig;
8
8
  exports.enhanceUI5DeployYaml = enhanceUI5DeployYaml;
9
9
  exports.enhanceManifestChangeContentWithFlpConfig = enhanceManifestChangeContentWithFlpConfig;
10
10
  exports.enhanceUI5YamlWithCfCustomTask = enhanceUI5YamlWithCfCustomTask;
11
- exports.enhanceUI5YamlWithCfCustomMiddleware = enhanceUI5YamlWithCfCustomMiddleware;
11
+ exports.enhanceUI5YamlWithFioriToolsMiddleware = enhanceUI5YamlWithFioriToolsMiddleware;
12
12
  const constants_1 = require("../base/constants");
13
13
  const VSCODE_URL = 'https://REQUIRED_FOR_VSCODE.example';
14
14
  /**
@@ -330,38 +330,22 @@ function enhanceUI5YamlWithCfCustomTask(ui5Config, config) {
330
330
  * @param {UI5Config} ui5Config - Configuration representing the ui5.yaml.
331
331
  * @param {CfAdpWriterConfig} config - Full project configuration.
332
332
  */
333
- function enhanceUI5YamlWithCfCustomMiddleware(ui5Config, config) {
333
+ /**
334
+ * Generate custom middleware configuration (fiori-tools-proxy and fiori-tools-preview only).
335
+ *
336
+ * @param {UI5Config} ui5Config - Configuration representing the ui5.yaml.
337
+ */
338
+ function enhanceUI5YamlWithFioriToolsMiddleware(ui5Config) {
334
339
  const ui5ConfigOptions = {
335
340
  url: constants_1.UI5_CDN_URL
336
341
  };
337
- const oauthPaths = config.cf?.oauthPaths;
338
- const backendUrl = config.cf?.backendUrl;
339
- if (oauthPaths && oauthPaths.length > 0 && backendUrl) {
340
- ui5Config.addCustomMiddleware([
341
- {
342
- name: 'backend-proxy-middleware-cf',
343
- afterMiddleware: 'compression',
344
- configuration: {
345
- url: backendUrl,
346
- paths: oauthPaths
347
- }
348
- }
349
- ]);
350
- ui5Config.addFioriToolsProxyMiddleware({
351
- ui5: ui5ConfigOptions,
352
- backend: []
353
- }, 'backend-proxy-middleware-cf');
354
- }
355
- else {
356
- ui5Config.addFioriToolsProxyMiddleware({
357
- ui5: ui5ConfigOptions,
358
- backend: []
359
- }, 'compression');
360
- }
342
+ // Add fiori-tools-appreload for live reload during development
343
+ ui5Config.addFioriToolsAppReloadMiddleware();
344
+ // Add fiori-tools-preview (for local preview)
361
345
  ui5Config.addCustomMiddleware([
362
346
  {
363
347
  name: 'fiori-tools-preview',
364
- afterMiddleware: 'fiori-tools-proxy',
348
+ afterMiddleware: 'fiori-tools-appreload',
365
349
  configuration: {
366
350
  flp: {
367
351
  theme: 'sap_horizon'
@@ -372,5 +356,10 @@ function enhanceUI5YamlWithCfCustomMiddleware(ui5Config, config) {
372
356
  }
373
357
  }
374
358
  ]);
359
+ // Add fiori-tools-proxy (for UI5 resources)
360
+ ui5Config.addFioriToolsProxyMiddleware({
361
+ ui5: ui5ConfigOptions,
362
+ backend: []
363
+ }, 'fiori-tools-preview');
375
364
  }
376
365
  //# sourceMappingURL=options.js.map
@@ -157,7 +157,8 @@ async function writeCfUI5Yaml(projectPath, data, fs) {
157
157
  /** Builder task */
158
158
  (0, options_1.enhanceUI5YamlWithCfCustomTask)(ui5Config, data);
159
159
  /** Middlewares */
160
- (0, options_1.enhanceUI5YamlWithCfCustomMiddleware)(ui5Config, data);
160
+ // Add fiori-tools-proxy and fiori-tools-preview for local development
161
+ (0, options_1.enhanceUI5YamlWithFioriToolsMiddleware)(ui5Config);
161
162
  fs.write(ui5ConfigPath, ui5Config.toString());
162
163
  }
163
164
  catch (e) {
@@ -118,8 +118,9 @@ function getCfConfig(params) {
118
118
  businessService: params.cfServicesAnswers.businessService ?? '',
119
119
  businessSolutionName: params.cfServicesAnswers.businessSolutionName,
120
120
  serviceInstanceGuid: params.serviceInstanceGuid,
121
- backendUrl: params.backendUrl,
122
- oauthPaths: params.oauthPaths
121
+ backendUrls: params.backendUrls,
122
+ oauthPaths: params.oauthPaths,
123
+ serviceInfo: params.serviceInfo
123
124
  },
124
125
  project: {
125
126
  name: params.attributeAnswers.projectName,
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "bugs": {
10
10
  "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Aadp-tooling"
11
11
  },
12
- "version": "0.18.61",
12
+ "version": "0.18.63",
13
13
  "license": "Apache-2.0",
14
14
  "author": "@SAP/ux-tools-team",
15
15
  "main": "dist/index.js",
@@ -39,14 +39,14 @@
39
39
  "@sap-ux/axios-extension": "1.25.11",
40
40
  "@sap-ux/btp-utils": "1.1.8",
41
41
  "@sap-ux/i18n": "0.3.7",
42
- "@sap-ux/inquirer-common": "0.11.3",
42
+ "@sap-ux/inquirer-common": "0.11.5",
43
43
  "@sap-ux/logger": "0.8.1",
44
44
  "@sap-ux/nodejs-utils": "0.2.14",
45
- "@sap-ux/odata-service-writer": "0.29.17",
46
- "@sap-ux/project-access": "1.35.2",
47
- "@sap-ux/project-input-validator": "0.6.54",
48
- "@sap-ux/store": "1.5.5",
49
- "@sap-ux/system-access": "0.6.50",
45
+ "@sap-ux/odata-service-writer": "0.29.18",
46
+ "@sap-ux/project-access": "1.35.3",
47
+ "@sap-ux/project-input-validator": "0.6.55",
48
+ "@sap-ux/store": "1.5.6",
49
+ "@sap-ux/system-access": "0.6.51",
50
50
  "@sap-ux/ui5-config": "0.29.16",
51
51
  "@sap-ux/ui5-info": "0.13.12"
52
52
  },
@@ -68,7 +68,7 @@
68
68
  "nock": "13.4.0",
69
69
  "rimraf": "6.1.2",
70
70
  "supertest": "7.1.4",
71
- "@sap-ux/store": "1.5.5"
71
+ "@sap-ux/store": "1.5.6"
72
72
  },
73
73
  "engines": {
74
74
  "node": ">=20.x"
@@ -2,4 +2,5 @@ node_modules/
2
2
  dist/
3
3
  .tmp
4
4
  .env
5
+ .adp
5
6
  *.zip
@@ -4,26 +4,32 @@
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "prestart": "npm run build",
8
- "start": "fiori run --open /test/flp.html#app-preview",
9
- "start-editor": "fiori run --open /test/adaptation-editor.html",
7
+ "start": "npm run setup-cf && npm run build && fiori run --open /test/flp.html#app-preview",
8
+ "start-nobuild": "fiori run --open /test/flp.html#app-preview",
9
+ "setup-cf": "sap-ux add adp-cf-config",
10
+ "start-editor-nobuild": "fiori run --open /test/adaptation-editor.html",
11
+ "start-editor": "npm run setup-cf && fiori run --open /test/adaptation-editor.html",
10
12
  "build": "npm run clean && ui5 build --include-task=generateCachebusterInfo && npm run zip",
11
13
  "zip": "cd dist && npx bestzip ../<%= module %>.zip *",
12
14
  "clean": "npx rimraf <%= module %>.zip dist",
13
- "build-ui5": "npm explore @ui5/task-adaptation -- npm run rollup"
15
+ "build-ui5": "npm explore @ui5/task-adaptation -- npm run rollup",
16
+ "build-mta": "rimraf resources mta_archives && mbt build --mtar archive --source ../",
17
+ "deploy": "cf deploy ../mta_archives/archive.mtar"
14
18
  },
15
19
  "repository": {
16
20
  "type": "git",
17
21
  "build": "ui5.js build --verbose --include-task generateCachebusterInfo"
18
22
  },
19
23
  "devDependencies": {
24
+ "@sap-ux/create": "^0.14.22",
20
25
  "@sap/ui5-builder-webide-extension": "^1.1.9",
21
26
  "@sap-ux/backend-proxy-middleware-cf": "0",
22
27
  "@sapui5/ts-types": "^1.141.2",
23
28
  "@sap/ux-ui5-tooling": "1",
24
29
  "@ui5/cli": "^4.0.33",
25
- "@ui5/task-adaptation": "^1.5.3",
30
+ "@ui5/task-adaptation": "1.6.0-rc.4",
26
31
  "bestzip": "^2.2.1",
27
- "rimraf": "^5.0.5"
32
+ "rimraf": "^6.1.2",
33
+ "mbt": "^1.2.34"
28
34
  }
29
35
  }