@uipath/packager-tool-webapp 1.204.0-preview.255 → 1.205.0-preview.256

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/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export type { WebAppManifest } from "./models/webapp-manifest.js";
2
2
  export { WEBAPP_MANIFEST_FILE_NAME, WebAppVariantType, } from "./models/webapp-manifest.js";
3
+ export { ensureWebAppProjectId } from "./utils/manifest-loader.js";
3
4
  export { WebAppTool } from "./webapp-tool.js";
4
5
  export { WebAppToolFactory } from "./webapp-tool-factory.js";
package/dist/index.js CHANGED
@@ -5,15 +5,93 @@ var WebAppVariantType;
5
5
  WebAppVariantType2["JS"] = "JS";
6
6
  })(WebAppVariantType ||= {});
7
7
  var WEBAPP_MANIFEST_FILE_NAME = "webAppManifest.json";
8
+ // src/utils/manifest-loader.ts
9
+ import { Path } from "@uipath/solutionpackager-tool-core";
10
+ async function loadWebAppManifest(fileSystem, projectPath) {
11
+ const manifestPath = Path.join(projectPath, WEBAPP_MANIFEST_FILE_NAME);
12
+ const exists = await fileSystem.exists(manifestPath);
13
+ if (!exists) {
14
+ return null;
15
+ }
16
+ const content = await fileSystem.readFile(manifestPath);
17
+ if (!content) {
18
+ return null;
19
+ }
20
+ try {
21
+ const text = new TextDecoder().decode(content);
22
+ const manifest = JSON.parse(text);
23
+ return manifest;
24
+ } catch (error) {
25
+ throw new Error(`Failed to parse ${WEBAPP_MANIFEST_FILE_NAME}: ${error instanceof Error ? error.message : String(error)}`);
26
+ }
27
+ }
28
+ async function ensureWebAppProjectId(fileSystem, projectPath) {
29
+ const manifest = await loadWebAppManifest(fileSystem, projectPath);
30
+ if (manifest && typeof manifest.projectId === "string" && manifest.projectId.length > 0) {
31
+ return manifest.projectId;
32
+ }
33
+ const id = crypto.randomUUID();
34
+ if (manifest) {
35
+ const updated = { ...manifest, projectId: id };
36
+ const manifestPath = Path.join(projectPath, WEBAPP_MANIFEST_FILE_NAME);
37
+ await fileSystem.writeFile(manifestPath, `${JSON.stringify(updated, null, 2)}
38
+ `);
39
+ }
40
+ return id;
41
+ }
8
42
  // src/webapp-tool.ts
9
43
  import {
10
- Path as Path5,
44
+ Path as Path6,
11
45
  ProjectTool,
12
46
  TemporaryStorageService,
13
47
  ToolErrorCodes,
14
48
  ToolResult
15
49
  } from "@uipath/solutionpackager-tool-core";
16
50
 
51
+ // src/utils/bundle-path.ts
52
+ import { Path as Path2 } from "@uipath/solutionpackager-tool-core";
53
+ function normalizeBundlePath(path) {
54
+ return path.replace(/\\/g, "/");
55
+ }
56
+ function isBundleRelativePath(path) {
57
+ const normalized = normalizeBundlePath(path);
58
+ const segments = normalized.split("/");
59
+ return !normalized.startsWith("/") && !/^[a-zA-Z]:/.test(normalized) && !segments.includes("..") && segments.some((segment) => segment !== "" && segment !== ".");
60
+ }
61
+ async function isCanonicalBundleChild(fileSystem, projectPath, bundlePath) {
62
+ const project = normalizeBundlePath(await fileSystem.realpath(projectPath)).replace(/\/+$/, "") || "/";
63
+ let bundle = project;
64
+ for (const segment of normalizeBundlePath(bundlePath).split("/")) {
65
+ if (!segment || segment === ".")
66
+ continue;
67
+ bundle = normalizeBundlePath(await fileSystem.realpath(Path2.join(bundle, segment))).replace(/\/+$/, "") || "/";
68
+ }
69
+ return bundle !== project && bundle.startsWith(project === "/" ? "/" : `${project}/`);
70
+ }
71
+ function isHtmlEntryPointPath(path) {
72
+ return typeof path === "string" && isBundleRelativePath(path) && /\.html?$/i.test(path);
73
+ }
74
+ async function resolveHtmlEntryPoint(fileSystem, folder, declaredPath) {
75
+ if (!isHtmlEntryPointPath(declaredPath))
76
+ return null;
77
+ const segments = normalizeBundlePath(declaredPath).split("/").filter((part) => part !== "" && part !== ".");
78
+ if (!(await fileSystem.stat(Path2.join(folder, ...segments)))?.isFile())
79
+ return null;
80
+ const actual = [];
81
+ for (const segment of segments) {
82
+ const entries = await fileSystem.readdir(Path2.join(folder, ...actual));
83
+ if (entries.includes(segment)) {
84
+ actual.push(segment);
85
+ continue;
86
+ }
87
+ const matches = entries.filter((entry) => entry.toLowerCase() === segment.toLowerCase());
88
+ if (matches.length !== 1)
89
+ return null;
90
+ actual.push(matches[0]);
91
+ }
92
+ return actual.join("/");
93
+ }
94
+
17
95
  // src/constants.ts
18
96
  var PROJECT_JSON_FILE = "project.json";
19
97
  var DEFAULT_BUNDLE_PATH = "source/dist";
@@ -22,12 +100,23 @@ var CONTENT_APP_FOLDER_NAME = "app";
22
100
  var TARGET_RUNTIME = "Coded";
23
101
  var TARGET_JS_RUNTIME = "JS";
24
102
  var DEFAULT_ENTRY_POINT_TYPE = "api";
103
+ var HTML_ENTRY_POINT_CANDIDATES = [
104
+ "index.html",
105
+ "main.html",
106
+ "app.html"
107
+ ];
25
108
  var ERROR_MESSAGES = {
26
109
  MANIFEST_NOT_FOUND: (file) => `WebApp manifest not found: ${file}. This file is required for WebApp projects.`,
27
110
  PROJECT_JSON_FOUND: "project.json found in WebApp project. The WebApp tool only supports Coded web apps without project.json.",
28
111
  MANIFEST_LOAD_FAILED: (file) => `Failed to load or parse ${file}`,
29
112
  BUNDLE_NOT_FOUND: (path) => `Compiled bundle not found at ${path}. Ensure the project is built before packing.`,
30
113
  BUNDLE_NOT_DIRECTORY: (path) => `Bundle path ${path} exists but is not a directory.`,
114
+ BUNDLE_OUTSIDE_PROJECT: (path) => `Bundle path ${path} resolves to the project root or outside the project. Use a child build output folder inside the project.`,
115
+ BUNDLE_NO_ENTRY_POINT: (path) => `Bundle at ${path} has no HTML entry point (${HTML_ENTRY_POINT_CANDIDATES.join(", ")}). Build the app before packing, or point config.bundlePath in webAppManifest.json at its build output.`,
116
+ MAIN_FILE_INVALID_TYPE: "Invalid webAppManifest.json: config.mainFile must be a string naming an HTML file, or be omitted.",
117
+ MAIN_FILE_NOT_IN_BUNDLE: (path, mainFile) => `${WEBAPP_MANIFEST_FILE_NAME} asks for "${mainFile}" as the app's main file, but the bundle at ${path} does not hold it. Name an HTML file inside the bundle in config.mainFile, or remove it to use the first of ${HTML_ENTRY_POINT_CANDIDATES.join(", ")} the bundle holds.`,
118
+ MAIN_FILE_LOST: (path, mainFile) => `Bundle at ${path} held "${mainFile}" when the pack started, but the copy taken for the package has not. Something changed the folder mid-pack — usually a build or watch process. Stop it and pack again.`,
119
+ BUNDLE_ENTRY_POINT_LOST: (path) => `Bundle at ${path} had an HTML entry point (${HTML_ENTRY_POINT_CANDIDATES.join(", ")}) when the pack started, but the copy taken for the package has none. Something changed the folder mid-pack — usually a build or watch process. Stop it and pack again.`,
31
120
  APP_FOLDER_NOT_FOUND: (path) => `.app folder not found at ${path}. Ensure the project contains a .app folder.`,
32
121
  APP_FOLDER_NOT_DIRECTORY: (path) => `.app path ${path} exists but is not a directory.`,
33
122
  BUILD_NOT_SUPPORTED: "Build execution (isCompiled=false) is not yet supported. Please build the project manually and set isCompiled=true.",
@@ -43,19 +132,19 @@ var ERROR_MESSAGES = {
43
132
  import {
44
133
  NugetConstants,
45
134
  NugetPackager,
46
- Path as Path3,
135
+ Path as Path4,
47
136
  ProjectTypes,
48
137
  TargetFramework
49
138
  } from "@uipath/solutionpackager-tool-core";
50
139
 
51
140
  // src/utils/fs-helpers.ts
52
- import { Path } from "@uipath/solutionpackager-tool-core";
141
+ import { Path as Path3 } from "@uipath/solutionpackager-tool-core";
53
142
  async function copyDirectoryAsync(fileSystem, sourcePath, destinationPath) {
54
143
  await fileSystem.mkdir(destinationPath);
55
144
  const entries = await fileSystem.readdir(sourcePath);
56
145
  for (const entry of entries) {
57
- const sourceEntry = Path.join(sourcePath, entry);
58
- const destEntry = Path.join(destinationPath, entry);
146
+ const sourceEntry = Path3.join(sourcePath, entry);
147
+ const destEntry = Path3.join(destinationPath, entry);
59
148
  const stat = await fileSystem.stat(sourceEntry);
60
149
  if (stat?.isDirectory()) {
61
150
  await copyDirectoryAsync(fileSystem, sourceEntry, destEntry);
@@ -68,41 +157,6 @@ async function copyDirectoryAsync(fileSystem, sourcePath, destinationPath) {
68
157
  }
69
158
  }
70
159
 
71
- // src/utils/manifest-loader.ts
72
- import { Path as Path2 } from "@uipath/solutionpackager-tool-core";
73
- async function loadWebAppManifest(fileSystem, projectPath) {
74
- const manifestPath = Path2.join(projectPath, WEBAPP_MANIFEST_FILE_NAME);
75
- const exists = await fileSystem.exists(manifestPath);
76
- if (!exists) {
77
- return null;
78
- }
79
- const content = await fileSystem.readFile(manifestPath);
80
- if (!content) {
81
- return null;
82
- }
83
- try {
84
- const text = new TextDecoder().decode(content);
85
- const manifest = JSON.parse(text);
86
- return manifest;
87
- } catch (error) {
88
- throw new Error(`Failed to parse ${WEBAPP_MANIFEST_FILE_NAME}: ${error instanceof Error ? error.message : String(error)}`);
89
- }
90
- }
91
- async function ensureWebAppProjectId(fileSystem, projectPath) {
92
- const manifest = await loadWebAppManifest(fileSystem, projectPath);
93
- if (manifest && typeof manifest.projectId === "string" && manifest.projectId.length > 0) {
94
- return manifest.projectId;
95
- }
96
- const id = crypto.randomUUID();
97
- if (manifest) {
98
- const updated = { ...manifest, projectId: id };
99
- const manifestPath = Path2.join(projectPath, WEBAPP_MANIFEST_FILE_NAME);
100
- await fileSystem.writeFile(manifestPath, `${JSON.stringify(updated, null, 2)}
101
- `);
102
- }
103
- return id;
104
- }
105
-
106
160
  // src/strategies/coded-app-strategy.ts
107
161
  class CodedAppStrategy {
108
162
  fileSystem;
@@ -118,10 +172,9 @@ class CodedAppStrategy {
118
172
  }
119
173
  const isCompiled = typed.config?.isCompiled ?? true;
120
174
  if (isCompiled) {
121
- const fullBundlePath = Path3.join(projectPath, bundlePath);
122
- const exists = await this.fileSystem.exists(fullBundlePath);
123
- if (!exists) {
124
- const message = ERROR_MESSAGES.BUNDLE_NOT_FOUND(fullBundlePath);
175
+ const fullBundlePath = Path4.join(projectPath, bundlePath);
176
+ const message = await this.describeBundleProblemAsync(fullBundlePath, typed.config?.mainFile);
177
+ if (message) {
125
178
  const warnable = logger;
126
179
  if (warnable?.warn) {
127
180
  warnable.warn(message);
@@ -139,7 +192,11 @@ class CodedAppStrategy {
139
192
  bundlePath = DEFAULT_BUNDLE_PATH;
140
193
  }
141
194
  const isCompiled = manifest.config?.isCompiled ?? true;
142
- const fullBundlePath = Path3.join(projectPath, bundlePath);
195
+ const declaredMainFile = manifest.config?.mainFile;
196
+ const fullBundlePath = Path4.join(projectPath, bundlePath);
197
+ if (isBundleRelativePath(bundlePath) && !await isCanonicalBundleChild(this.fileSystem, projectPath, bundlePath)) {
198
+ throw new Error(ERROR_MESSAGES.BUNDLE_OUTSIDE_PROJECT(bundlePath));
199
+ }
143
200
  if (isCompiled) {
144
201
  logger?.progress("Validating compiled bundle...");
145
202
  const bundleExists = await this.fileSystem.exists(fullBundlePath);
@@ -150,21 +207,25 @@ class CodedAppStrategy {
150
207
  if (!bundleStat?.isDirectory()) {
151
208
  throw new Error(ERROR_MESSAGES.BUNDLE_NOT_DIRECTORY(fullBundlePath));
152
209
  }
210
+ const problem = await this.describeBundleEntryProblemAsync(fullBundlePath, declaredMainFile);
211
+ if (problem) {
212
+ throw new Error(problem);
213
+ }
153
214
  } else {
154
215
  logger?.info("Build mode (isCompiled=false) is not yet implemented in v1.");
155
216
  throw new Error(ERROR_MESSAGES.BUILD_NOT_SUPPORTED);
156
217
  }
157
- const localBuildFolder = Path3.join(outputPath, NugetConstants.OutputFolderName);
158
- const contentFolder = Path3.join(localBuildFolder, NugetConstants.ContentFolderName);
159
- await this.fileSystem.mkdir(contentFolder);
218
+ const localBuildFolder = Path4.join(outputPath, `${NugetConstants.OutputFolderName}-${crypto.randomUUID()}`);
219
+ const contentFolder = Path4.join(localBuildFolder, NugetConstants.ContentFolderName);
160
220
  try {
221
+ await this.fileSystem.mkdir(contentFolder);
161
222
  logger?.progress("Copying bundle to content folder...");
162
223
  await copyDirectoryAsync(this.fileSystem, fullBundlePath, contentFolder);
163
224
  logger?.progress("Preparing metadata files...");
164
- await this.prepareMetadataFiles(localBuildFolder, contentFolder, packageInfo, manifest, projectPath);
225
+ await this.prepareMetadataFiles(localBuildFolder, contentFolder, packageInfo, manifest, projectPath, fullBundlePath);
165
226
  logger?.progress("Creating NuGet package...");
166
227
  const nupkgFileName = `${packageInfo.id}.${packageInfo.version}.nupkg`;
167
- const nupkgPath = Path3.join(outputPath, nupkgFileName);
228
+ const nupkgPath = Path4.join(outputPath, nupkgFileName);
168
229
  const packager = new NugetPackager(this.fileSystem);
169
230
  const result = await packager.packAsync(localBuildFolder, packageInfo, nupkgPath);
170
231
  logger?.info(`Package created successfully: ${result.outputPath}`);
@@ -177,21 +238,13 @@ class CodedAppStrategy {
177
238
  }
178
239
  }
179
240
  }
180
- async prepareMetadataFiles(_localBuildFolder, contentFolder, _packageInfo, _manifest, projectPath) {
181
- let mainFile = "index.html";
182
- const indexHtmlPath = Path3.join(contentFolder, "index.html");
183
- const indexHtmlExists = await this.fileSystem.exists(indexHtmlPath);
184
- if (!indexHtmlExists) {
185
- const possibleEntries = ["index.html", "main.html", "app.html"];
186
- for (const entry of possibleEntries) {
187
- const entryPath = Path3.join(contentFolder, entry);
188
- if (await this.fileSystem.exists(entryPath)) {
189
- mainFile = entry;
190
- break;
191
- }
192
- }
241
+ async prepareMetadataFiles(_localBuildFolder, contentFolder, _packageInfo, manifest, projectPath, sourceBundlePath) {
242
+ const declaredMainFile = manifest.config?.mainFile;
243
+ const mainFile = await this.resolveMainFileAsync(contentFolder, declaredMainFile);
244
+ if (!mainFile) {
245
+ throw new Error(declaredMainFile ? ERROR_MESSAGES.MAIN_FILE_LOST(sourceBundlePath, declaredMainFile) : ERROR_MESSAGES.BUNDLE_ENTRY_POINT_LOST(sourceBundlePath));
193
246
  }
194
- const operatePath = Path3.join(contentFolder, NugetConstants.OperateFileName);
247
+ const operatePath = Path4.join(contentFolder, NugetConstants.OperateFileName);
195
248
  const operateModel = {
196
249
  projectId: await ensureWebAppProjectId(this.fileSystem, projectPath),
197
250
  main: mainFile,
@@ -205,17 +258,13 @@ class CodedAppStrategy {
205
258
  };
206
259
  const operateJson = JSON.stringify(operateModel, null, 2);
207
260
  await this.fileSystem.writeFile(operatePath, operateJson);
208
- const manifestSourcePath = Path3.join(projectPath, WEBAPP_MANIFEST_FILE_NAME);
209
- const manifestDestPath = Path3.join(contentFolder, WEBAPP_MANIFEST_FILE_NAME);
210
- const manifestExists = await this.fileSystem.exists(manifestSourcePath);
211
- if (manifestExists) {
212
- const manifestContent = await this.fileSystem.readFile(manifestSourcePath, "utf-8");
213
- if (manifestContent) {
214
- await this.fileSystem.writeFile(manifestDestPath, manifestContent);
215
- }
216
- }
217
- const uipathJsonSourcePath = Path3.join(projectPath, "uipath.json");
218
- const uipathJsonDestPath = Path3.join(contentFolder, "uipath.json");
261
+ await this.fileSystem.writeFile(Path4.join(contentFolder, WEBAPP_MANIFEST_FILE_NAME), JSON.stringify({
262
+ ...manifest,
263
+ config: { ...manifest.config, mainFile },
264
+ projectId: operateModel.projectId
265
+ }, null, 2));
266
+ const uipathJsonSourcePath = Path4.join(projectPath, "uipath.json");
267
+ const uipathJsonDestPath = Path4.join(contentFolder, "uipath.json");
219
268
  const uipathJsonExists = await this.fileSystem.exists(uipathJsonSourcePath);
220
269
  if (uipathJsonExists) {
221
270
  const uipathJsonContent = await this.fileSystem.readFile(uipathJsonSourcePath, "utf-8");
@@ -223,8 +272,8 @@ class CodedAppStrategy {
223
272
  await this.fileSystem.writeFile(uipathJsonDestPath, uipathJsonContent);
224
273
  }
225
274
  }
226
- const actionSchemaSourcePath = Path3.join(projectPath, "action-schema.json");
227
- const actionSchemaDestPath = Path3.join(contentFolder, "action-schema.json");
275
+ const actionSchemaSourcePath = Path4.join(projectPath, "action-schema.json");
276
+ const actionSchemaDestPath = Path4.join(contentFolder, "action-schema.json");
228
277
  const actionSchemaExists = await this.fileSystem.exists(actionSchemaSourcePath);
229
278
  if (actionSchemaExists) {
230
279
  const actionSchemaContent = await this.fileSystem.readFile(actionSchemaSourcePath, "utf-8");
@@ -232,7 +281,7 @@ class CodedAppStrategy {
232
281
  await this.fileSystem.writeFile(actionSchemaDestPath, actionSchemaContent);
233
282
  }
234
283
  }
235
- const bindingsPath = Path3.join(contentFolder, "bindings.json");
284
+ const bindingsPath = Path4.join(contentFolder, "bindings.json");
236
285
  const bindingsExists = await this.fileSystem.exists(bindingsPath);
237
286
  if (!bindingsExists) {
238
287
  const bindingsJson = JSON.stringify({
@@ -241,12 +290,12 @@ class CodedAppStrategy {
241
290
  }, null, 2);
242
291
  await this.fileSystem.writeFile(bindingsPath, bindingsJson);
243
292
  }
244
- const bindingsV2Path = Path3.join(contentFolder, "bindings_v2.json");
293
+ const bindingsV2Path = Path4.join(contentFolder, "bindings_v2.json");
245
294
  const bindingsCandidatePaths = [
246
- Path3.join(projectPath, "source", "bindings_v2.json"),
247
- Path3.join(projectPath, "source", "bindings.json"),
248
- Path3.join(projectPath, "bindings.json"),
249
- Path3.join(projectPath, "bindings_v2.json")
295
+ Path4.join(projectPath, "source", "bindings_v2.json"),
296
+ Path4.join(projectPath, "source", "bindings.json"),
297
+ Path4.join(projectPath, "bindings.json"),
298
+ Path4.join(projectPath, "bindings_v2.json")
250
299
  ];
251
300
  let bindingsV2Json = null;
252
301
  for (const candidate of bindingsCandidatePaths) {
@@ -266,14 +315,14 @@ class CodedAppStrategy {
266
315
  }, null, 2);
267
316
  }
268
317
  await this.fileSystem.writeFile(bindingsV2Path, bindingsV2Json);
269
- const entryPointsPath = Path3.join(contentFolder, "entry-points.json");
270
- const projectEntryPointsPath = Path3.join(projectPath, "entry-points.json");
318
+ const entryPointsPath = Path4.join(contentFolder, "entry-points.json");
319
+ const projectEntryPointsPath = Path4.join(projectPath, "entry-points.json");
271
320
  let entryPointsContent;
272
321
  const projectEntryPointsExists = await this.fileSystem.exists(projectEntryPointsPath);
273
322
  if (projectEntryPointsExists) {
274
323
  const entryPointsData = await this.fileSystem.readFile(projectEntryPointsPath, "utf-8");
275
324
  if (entryPointsData) {
276
- entryPointsContent = entryPointsData;
325
+ entryPointsContent = this.alignEntryPoint(entryPointsData, mainFile);
277
326
  } else {
278
327
  entryPointsContent = this.createDefaultEntryPoints(mainFile);
279
328
  }
@@ -281,18 +330,68 @@ class CodedAppStrategy {
281
330
  entryPointsContent = this.createDefaultEntryPoints(mainFile);
282
331
  }
283
332
  await this.fileSystem.writeFile(entryPointsPath, entryPointsContent);
284
- const packageDescriptorPath = Path3.join(contentFolder, NugetConstants.PackageDescriptorFileName);
333
+ const packageDescriptorPath = Path4.join(contentFolder, NugetConstants.PackageDescriptorFileName);
285
334
  const packageDescriptor = {
286
335
  $schema: "https://cloud.uipath.com/draft/2024-12/package-descriptor",
287
336
  files: {
288
- [NugetConstants.OperateFileName]: Path3.join(NugetConstants.ContentFolderName, NugetConstants.OperateFileName),
289
- "entry-points.json": Path3.join(NugetConstants.ContentFolderName, "entry-points.json"),
290
- "bindings.json": Path3.join(NugetConstants.ContentFolderName, "bindings_v2.json")
337
+ [NugetConstants.OperateFileName]: Path4.join(NugetConstants.ContentFolderName, NugetConstants.OperateFileName),
338
+ "entry-points.json": Path4.join(NugetConstants.ContentFolderName, "entry-points.json"),
339
+ "bindings.json": Path4.join(NugetConstants.ContentFolderName, "bindings_v2.json")
291
340
  }
292
341
  };
293
342
  const packageDescriptorJson = JSON.stringify(packageDescriptor, null, 2);
294
343
  await this.fileSystem.writeFile(packageDescriptorPath, packageDescriptorJson);
295
344
  }
345
+ async describeBundleProblemAsync(fullBundlePath, declaredMainFile) {
346
+ const bundleStat = await this.fileSystem.stat(fullBundlePath);
347
+ if (!bundleStat) {
348
+ return ERROR_MESSAGES.BUNDLE_NOT_FOUND(fullBundlePath);
349
+ }
350
+ if (!bundleStat.isDirectory()) {
351
+ return ERROR_MESSAGES.BUNDLE_NOT_DIRECTORY(fullBundlePath);
352
+ }
353
+ return this.describeBundleEntryProblemAsync(fullBundlePath, declaredMainFile);
354
+ }
355
+ async describeBundleEntryProblemAsync(fullBundlePath, declaredMainFile) {
356
+ if (declaredMainFile !== undefined && typeof declaredMainFile !== "string") {
357
+ return ERROR_MESSAGES.MAIN_FILE_INVALID_TYPE;
358
+ }
359
+ if (await this.resolveMainFileAsync(fullBundlePath, declaredMainFile)) {
360
+ return null;
361
+ }
362
+ return declaredMainFile !== undefined ? ERROR_MESSAGES.MAIN_FILE_NOT_IN_BUNDLE(fullBundlePath, declaredMainFile) : ERROR_MESSAGES.BUNDLE_NO_ENTRY_POINT(fullBundlePath);
363
+ }
364
+ async resolveMainFileAsync(folder, declaredMainFile) {
365
+ if (declaredMainFile === undefined) {
366
+ return this.findEntryPointAsync(folder);
367
+ }
368
+ return resolveHtmlEntryPoint(this.fileSystem, folder, declaredMainFile);
369
+ }
370
+ async findEntryPointAsync(folder) {
371
+ for (const candidate of HTML_ENTRY_POINT_CANDIDATES) {
372
+ const resolved = await resolveHtmlEntryPoint(this.fileSystem, folder, candidate);
373
+ if (resolved !== null)
374
+ return resolved;
375
+ }
376
+ return null;
377
+ }
378
+ alignEntryPoint(content, mainFile) {
379
+ const document = JSON.parse(content);
380
+ if (typeof document !== "object" || document === null || !("entryPoints" in document) || !Array.isArray(document.entryPoints)) {
381
+ throw new Error("Invalid entry-points.json: expected an entryPoints array.");
382
+ }
383
+ const primary = document.entryPoints[0];
384
+ if (typeof primary !== "object" || primary === null || Array.isArray(primary)) {
385
+ throw new Error("Invalid entry-points.json: expected a primary entry object.");
386
+ }
387
+ return JSON.stringify({
388
+ ...document,
389
+ entryPoints: [
390
+ { ...primary, filePath: mainFile },
391
+ ...document.entryPoints.slice(1)
392
+ ]
393
+ }, null, 2);
394
+ }
296
395
  createDefaultEntryPoints(mainFile) {
297
396
  const uniqueId = this.generateUniqueId();
298
397
  const entryPoints = {
@@ -339,7 +438,7 @@ class CodedAppStrategy {
339
438
  import {
340
439
  NugetConstants as NugetConstants2,
341
440
  NugetPackager as NugetPackager2,
342
- Path as Path4,
441
+ Path as Path5,
343
442
  ProjectTypes as ProjectTypes2,
344
443
  TargetFramework as TargetFramework2
345
444
  } from "@uipath/solutionpackager-tool-core";
@@ -350,7 +449,7 @@ class JsAppsStrategy {
350
449
  }
351
450
  async validateAsync(args) {
352
451
  const { projectPath, logger } = args;
353
- const appFolder = Path4.join(projectPath, APP_FOLDER_NAME);
452
+ const appFolder = Path5.join(projectPath, APP_FOLDER_NAME);
354
453
  const exists = await this.fileSystem.exists(appFolder);
355
454
  if (!exists) {
356
455
  const message = ERROR_MESSAGES.APP_FOLDER_NOT_FOUND(appFolder);
@@ -374,7 +473,7 @@ class JsAppsStrategy {
374
473
  async packageAsync(args) {
375
474
  const { projectPath, manifest, outputPath, packageInfo, logger } = args;
376
475
  logger?.info(`Packaging JS variant: ${packageInfo.id}@${packageInfo.version}`);
377
- const appFolder = Path4.join(projectPath, APP_FOLDER_NAME);
476
+ const appFolder = Path5.join(projectPath, APP_FOLDER_NAME);
378
477
  logger?.progress("Validating .app folder...");
379
478
  const appFolderExists = await this.fileSystem.exists(appFolder);
380
479
  if (!appFolderExists) {
@@ -384,9 +483,9 @@ class JsAppsStrategy {
384
483
  if (!appFolderStat?.isDirectory()) {
385
484
  throw new Error(ERROR_MESSAGES.APP_FOLDER_NOT_DIRECTORY(appFolder));
386
485
  }
387
- const localBuildFolder = Path4.join(outputPath, NugetConstants2.OutputFolderName);
388
- const contentFolder = Path4.join(localBuildFolder, NugetConstants2.ContentFolderName);
389
- const contentAppFolder = Path4.join(contentFolder, CONTENT_APP_FOLDER_NAME);
486
+ const localBuildFolder = Path5.join(outputPath, NugetConstants2.OutputFolderName);
487
+ const contentFolder = Path5.join(localBuildFolder, NugetConstants2.ContentFolderName);
488
+ const contentAppFolder = Path5.join(contentFolder, CONTENT_APP_FOLDER_NAME);
390
489
  await this.fileSystem.mkdir(contentFolder);
391
490
  try {
392
491
  logger?.progress("Copying .app folder into content/app...");
@@ -395,7 +494,7 @@ class JsAppsStrategy {
395
494
  await this.prepareMetadataFiles(contentFolder, contentAppFolder, packageInfo, manifest, projectPath);
396
495
  logger?.progress("Creating NuGet package...");
397
496
  const nupkgFileName = `${packageInfo.id}.${packageInfo.version}.nupkg`;
398
- const nupkgPath = Path4.join(outputPath, nupkgFileName);
497
+ const nupkgPath = Path5.join(outputPath, nupkgFileName);
399
498
  const packager = new NugetPackager2(this.fileSystem);
400
499
  const result = await packager.packAsync(localBuildFolder, packageInfo, nupkgPath);
401
500
  logger?.info(`Package created successfully: ${result.outputPath}`);
@@ -411,7 +510,7 @@ class JsAppsStrategy {
411
510
  async prepareMetadataFiles(contentFolder, _contentAppFolder, _packageInfo, _manifest, projectPath) {
412
511
  const mainFile = "index.html";
413
512
  const mainRelativeToContent = `${CONTENT_APP_FOLDER_NAME}/${mainFile}`;
414
- const operatePath = Path4.join(contentFolder, NugetConstants2.OperateFileName);
513
+ const operatePath = Path5.join(contentFolder, NugetConstants2.OperateFileName);
415
514
  const operateModel = {
416
515
  projectId: await ensureWebAppProjectId(this.fileSystem, projectPath),
417
516
  main: mainRelativeToContent,
@@ -425,8 +524,8 @@ class JsAppsStrategy {
425
524
  };
426
525
  const operateJson = JSON.stringify(operateModel, null, 2);
427
526
  await this.fileSystem.writeFile(operatePath, operateJson);
428
- const manifestSourcePath = Path4.join(projectPath, WEBAPP_MANIFEST_FILE_NAME);
429
- const manifestDestPath = Path4.join(contentFolder, WEBAPP_MANIFEST_FILE_NAME);
527
+ const manifestSourcePath = Path5.join(projectPath, WEBAPP_MANIFEST_FILE_NAME);
528
+ const manifestDestPath = Path5.join(contentFolder, WEBAPP_MANIFEST_FILE_NAME);
430
529
  const manifestExists = await this.fileSystem.exists(manifestSourcePath);
431
530
  if (manifestExists) {
432
531
  const manifestContent = await this.fileSystem.readFile(manifestSourcePath);
@@ -434,8 +533,8 @@ class JsAppsStrategy {
434
533
  await this.fileSystem.writeFile(manifestDestPath, manifestContent);
435
534
  }
436
535
  }
437
- const uipathJsonSourcePath = Path4.join(projectPath, "uipath.json");
438
- const uipathJsonDestPath = Path4.join(contentFolder, "uipath.json");
536
+ const uipathJsonSourcePath = Path5.join(projectPath, "uipath.json");
537
+ const uipathJsonDestPath = Path5.join(contentFolder, "uipath.json");
439
538
  const uipathJsonExists = await this.fileSystem.exists(uipathJsonSourcePath);
440
539
  if (uipathJsonExists) {
441
540
  const uipathJsonContent = await this.fileSystem.readFile(uipathJsonSourcePath);
@@ -443,20 +542,20 @@ class JsAppsStrategy {
443
542
  await this.fileSystem.writeFile(uipathJsonDestPath, uipathJsonContent);
444
543
  }
445
544
  }
446
- const bindingsPath = Path4.join(contentFolder, "bindings.json");
545
+ const bindingsPath = Path5.join(contentFolder, "bindings.json");
447
546
  const bindingsExists = await this.fileSystem.exists(bindingsPath);
448
547
  if (!bindingsExists) {
449
548
  const bindingsJson = JSON.stringify({ version: "1.0", resources: [] }, null, 2);
450
549
  await this.fileSystem.writeFile(bindingsPath, bindingsJson);
451
550
  }
452
- const bindingsV2Path = Path4.join(contentFolder, "bindings_v2.json");
551
+ const bindingsV2Path = Path5.join(contentFolder, "bindings_v2.json");
453
552
  const bindingsV2Exists = await this.fileSystem.exists(bindingsV2Path);
454
553
  if (!bindingsV2Exists) {
455
554
  const bindingsV2Json = JSON.stringify({ version: "2.0", resources: [] }, null, 2);
456
555
  await this.fileSystem.writeFile(bindingsV2Path, bindingsV2Json);
457
556
  }
458
- const entryPointsPath = Path4.join(contentFolder, "entry-points.json");
459
- const projectEntryPointsPath = Path4.join(projectPath, "entry-points.json");
557
+ const entryPointsPath = Path5.join(contentFolder, "entry-points.json");
558
+ const projectEntryPointsPath = Path5.join(projectPath, "entry-points.json");
460
559
  let entryPointsContent;
461
560
  const projectEntryPointsExists = await this.fileSystem.exists(projectEntryPointsPath);
462
561
  if (projectEntryPointsExists) {
@@ -470,12 +569,12 @@ class JsAppsStrategy {
470
569
  entryPointsContent = this.createDefaultEntryPoints(mainRelativeToContent);
471
570
  }
472
571
  await this.fileSystem.writeFile(entryPointsPath, entryPointsContent);
473
- const packageDescriptorPath = Path4.join(contentFolder, NugetConstants2.PackageDescriptorFileName);
572
+ const packageDescriptorPath = Path5.join(contentFolder, NugetConstants2.PackageDescriptorFileName);
474
573
  const packageDescriptor = {
475
574
  files: {
476
- [NugetConstants2.OperateFileName]: Path4.join(NugetConstants2.ContentFolderName, NugetConstants2.OperateFileName),
477
- "entry-points.json": Path4.join(NugetConstants2.ContentFolderName, "entry-points.json"),
478
- "bindings.json": Path4.join(NugetConstants2.ContentFolderName, "bindings_v2.json")
575
+ [NugetConstants2.OperateFileName]: Path5.join(NugetConstants2.ContentFolderName, NugetConstants2.OperateFileName),
576
+ "entry-points.json": Path5.join(NugetConstants2.ContentFolderName, "entry-points.json"),
577
+ "bindings.json": Path5.join(NugetConstants2.ContentFolderName, "bindings_v2.json")
479
578
  }
480
579
  };
481
580
  const packageDescriptorJson = JSON.stringify(packageDescriptor, null, 2);
@@ -605,7 +704,7 @@ class WebAppTool extends ProjectTool {
605
704
  let manifest;
606
705
  let strategy;
607
706
  try {
608
- const prepared = await this.getManifestAndStrategy(options.projectPath);
707
+ const prepared = await this.getManifestAndStrategy(options.projectPath, options.codedBundle);
609
708
  manifest = prepared.manifest;
610
709
  strategy = prepared.strategy;
611
710
  } catch (error) {
@@ -635,20 +734,33 @@ class WebAppTool extends ProjectTool {
635
734
  await this._temporaryStorage.cleanup();
636
735
  } catch {}
637
736
  }
638
- async getManifestAndStrategy(projectPath) {
639
- const manifestPath = Path5.join(projectPath, WEBAPP_MANIFEST_FILE_NAME);
737
+ async getManifestAndStrategy(projectPath, codedBundle) {
738
+ const manifestPath = Path6.join(projectPath, WEBAPP_MANIFEST_FILE_NAME);
640
739
  const manifestExists = await this.fileSystem.exists(manifestPath);
641
- if (!manifestExists) {
740
+ if (!manifestExists && !codedBundle) {
642
741
  throw new Error(ERROR_MESSAGES.MANIFEST_NOT_FOUND(WEBAPP_MANIFEST_FILE_NAME));
643
742
  }
644
- const projectJsonPath = Path5.join(projectPath, PROJECT_JSON_FILE);
743
+ const projectJsonPath = Path6.join(projectPath, PROJECT_JSON_FILE);
645
744
  if (await this.fileSystem.exists(projectJsonPath)) {
646
745
  throw new Error(ERROR_MESSAGES.PROJECT_JSON_FOUND);
647
746
  }
648
- const manifest = await loadWebAppManifest(this.fileSystem, projectPath);
747
+ let manifest = manifestExists ? await loadWebAppManifest(this.fileSystem, projectPath) : {
748
+ type: "Coded" /* Coded */,
749
+ solutionResourceSubType: "Coded" /* Coded */,
750
+ config: { isCompiled: true }
751
+ };
649
752
  if (!manifest) {
650
753
  throw new Error(ERROR_MESSAGES.MANIFEST_LOAD_FAILED(WEBAPP_MANIFEST_FILE_NAME));
651
754
  }
755
+ if (codedBundle) {
756
+ if (manifest.type !== "Coded" /* Coded */) {
757
+ throw new Error("A coded bundle requires a Coded webAppManifest.json.");
758
+ }
759
+ manifest = {
760
+ ...manifest,
761
+ config: { ...manifest.config, ...codedBundle }
762
+ };
763
+ }
652
764
  const strategy = VariantStrategyFactory.createStrategy(manifest, this.fileSystem);
653
765
  return { manifest, strategy };
654
766
  }
@@ -688,7 +800,8 @@ export {
688
800
  WEBAPP_MANIFEST_FILE_NAME,
689
801
  WebAppTool,
690
802
  WebAppToolFactory,
691
- WebAppVariantType
803
+ WebAppVariantType,
804
+ ensureWebAppProjectId
692
805
  };
693
806
 
694
- //# debugId=117BAA70A6FBCC6564756E2164756E21
807
+ //# debugId=AFDD418216D2D37664756E2164756E21
@@ -17,6 +17,14 @@ export interface CodedAppConfig {
17
17
  isCompiled?: boolean;
18
18
  /** Compiled bundle folder location (default: dist/) */
19
19
  bundlePath?: string;
20
+ /**
21
+ * The file inside the bundle that `operate.json` names as `main`. Omit it
22
+ * to let pack take the first of the default HTML candidates
23
+ * (index.html, main.html, app.html) the bundle holds. Set, it may be any HTML file in the bundle, and pack
24
+ * refuses the project when the bundle does not hold it — a `main` the
25
+ * package does not carry produces an app that cannot boot.
26
+ */
27
+ mainFile?: string;
20
28
  /** Optional build command for future scenarios */
21
29
  buildCommand?: string;
22
30
  isActionApp?: boolean;
@@ -26,6 +26,41 @@ export declare class CodedAppStrategy implements VariantStrategy {
26
26
  * Prepare metadata files (operate.json, package-descriptor.json, bindings.json, bindings_v2.json, entry-points.json)
27
27
  */
28
28
  private prepareMetadataFiles;
29
+ /**
30
+ * Why this bundle folder cannot be packed, or `null` when it can. Shared
31
+ * by `validateAsync` and the pack-time guard so the two never disagree
32
+ * about what counts as a usable bundle.
33
+ */
34
+ private describeBundleProblemAsync;
35
+ /**
36
+ * Why this bundle's entry point cannot be used, or `null` when it can. A
37
+ * `config.mainFile` the bundle does not hold is refused by name: falling
38
+ * back to `index.html` there would pack an app that boots a file the user
39
+ * did not ask for.
40
+ */
41
+ private describeBundleEntryProblemAsync;
42
+ /**
43
+ * The file `operate.json` names as `main`, or `null` when the folder does
44
+ * not hold it. A declared `config.mainFile` wins and may be any HTML file in
45
+ * the bundle, not only one of the three defaults; without one, the first
46
+ * default candidate present is used.
47
+ */
48
+ private resolveMainFileAsync;
49
+ /**
50
+ * The first HTML entry point present in a folder, or `null` when it holds
51
+ * none. One list, shared by the pack-time guard and the `operate.json`
52
+ * `main` lookup, so the file pack insists on is the file it then names.
53
+ *
54
+ * `stat().isFile()`, not `exists()`: `exists()` is true for a directory
55
+ * too, so a folder named `index.html` would satisfy the guard and then be
56
+ * named as `operate.json`'s `main`, which cannot boot.
57
+ */
58
+ private findEntryPointAsync;
59
+ /**
60
+ * Keep authored schemas and entry metadata; only the primary entry follows
61
+ * the HTML file selected for operate.json. The project file stays untouched.
62
+ */
63
+ private alignEntryPoint;
29
64
  /**
30
65
  * Create default entry-points.json structure
31
66
  */