@uipath/packager-tool-webapp 1.0.4
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/constants.d.ts +36 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +466 -0
- package/dist/models/webapp-manifest.d.ts +39 -0
- package/dist/strategies/coded-app-strategy.d.ts +37 -0
- package/dist/strategies/variant-strategy-factory.d.ts +16 -0
- package/dist/strategies/variant-strategy.d.ts +57 -0
- package/dist/utils/fs-helpers.d.ts +9 -0
- package/dist/utils/manifest-loader.d.ts +6 -0
- package/dist/webapp-tool-factory.d.ts +8 -0
- package/dist/webapp-tool.d.ts +29 -0
- package/package.json +58 -0
- package/src/constants.ts +60 -0
- package/src/index.ts +7 -0
- package/src/models/webapp-manifest.ts +45 -0
- package/src/strategies/coded-app-strategy.ts +413 -0
- package/src/strategies/variant-strategy-factory.ts +39 -0
- package/src/strategies/variant-strategy.ts +67 -0
- package/src/utils/fs-helpers.ts +58 -0
- package/src/utils/manifest-loader.ts +34 -0
- package/src/webapp-tool-factory.ts +23 -0
- package/src/webapp-tool.ts +269 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type IFileSystem, type IProjectBuildOptions, type IProjectPackOptions, type IProjectRestoreOptions, type IProjectValidateOptions, type IToolLogger, ProjectTool, ToolResult } from "@uipath/solutionpackager-tool-core";
|
|
2
|
+
/**
|
|
3
|
+
* WebApp project tool implementation
|
|
4
|
+
* Packages WebApp-type projects into .nupkg artifacts
|
|
5
|
+
*/
|
|
6
|
+
export declare class WebAppTool extends ProjectTool {
|
|
7
|
+
private readonly _temporaryStorage;
|
|
8
|
+
private _tempBuildFolder;
|
|
9
|
+
constructor(fileSystem: IFileSystem, logger: IToolLogger);
|
|
10
|
+
restoreAsync(_options: IProjectRestoreOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
|
|
11
|
+
validateAsync(options: IProjectValidateOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
|
|
12
|
+
buildAsync(_options: IProjectBuildOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
|
|
13
|
+
packAsync(options: IProjectPackOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
|
|
14
|
+
dispose(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Common helper that validates manifest file presence, loads manifest
|
|
17
|
+
* and instantiates a variant strategy. Throws Error with user-friendly
|
|
18
|
+
* message when any step fails.
|
|
19
|
+
*/
|
|
20
|
+
private getManifestAndStrategy;
|
|
21
|
+
/**
|
|
22
|
+
* Standardized error handling helper
|
|
23
|
+
*/
|
|
24
|
+
private handleError;
|
|
25
|
+
/**
|
|
26
|
+
* Create a logger wrapper for strategy methods
|
|
27
|
+
*/
|
|
28
|
+
private createStrategyLogger;
|
|
29
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uipath/packager-tool-webapp",
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "UiPath WebApp tool implementation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"source": "./src/index.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/UiPath/cli.git",
|
|
15
|
+
"directory": "packages/packager/packager-tool-webapp"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"registry": "https://registry.npmjs.org/"
|
|
19
|
+
},
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "bun build ./src/index.ts --outdir dist --format esm --target browser --external @uipath/solutionpackager-tool-core && tsc --emitDeclarationOnly --outDir dist",
|
|
23
|
+
"dev": "bun build ./src/index.ts --outdir dist --format esm --target browser --external @uipath/solutionpackager-tool-core --watch",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:browser": "vitest run --config=vitest.browser.config.ts",
|
|
26
|
+
"test:coverage": "vitest run --coverage",
|
|
27
|
+
"test:browser:coverage": "vitest run --config=vitest.browser.config.ts --coverage",
|
|
28
|
+
"test:all": "bun run test && bun run test:browser",
|
|
29
|
+
"test:all:coverage": "bun run test:coverage && bun run test:browser:coverage",
|
|
30
|
+
"prepack": "bun run build",
|
|
31
|
+
"publish:dry": "bun publish --dry-run",
|
|
32
|
+
"publish:gh": "bun publish",
|
|
33
|
+
"version:patch": "bun version patch --no-git-tag-version",
|
|
34
|
+
"version:minor": "bun version minor --no-git-tag-version",
|
|
35
|
+
"version:major": "bun version major --no-git-tag-version",
|
|
36
|
+
"lint": "biome check ."
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"!dist/**/*.map",
|
|
41
|
+
"src"
|
|
42
|
+
],
|
|
43
|
+
"author": "",
|
|
44
|
+
"license": "ISC",
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@uipath/solutionpackager-tool-core": "0.0.31"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^25.5.0",
|
|
50
|
+
"@uipath/solutionpackager-tool-core": "0.0.31",
|
|
51
|
+
"@vitest/browser": "^4.0.14",
|
|
52
|
+
"@vitest/browser-playwright": "^4.0.14",
|
|
53
|
+
"@vitest/coverage-v8": "^4.0.14",
|
|
54
|
+
"playwright": "^1.57.0",
|
|
55
|
+
"typescript": "^5.9.3",
|
|
56
|
+
"vitest": "^4.0.14"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants used throughout the WebApp tool implementation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* File names
|
|
7
|
+
*/
|
|
8
|
+
export const PROJECT_JSON_FILE = "project.json";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default bundle path if not specified in manifest
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_BUNDLE_PATH = "source/dist";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Target runtime for operate.json
|
|
17
|
+
*/
|
|
18
|
+
export const TARGET_RUNTIME = "Coded";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Default entry point type
|
|
22
|
+
*/
|
|
23
|
+
export const DEFAULT_ENTRY_POINT_TYPE = "api";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Error messages
|
|
27
|
+
*/
|
|
28
|
+
export const ERROR_MESSAGES = {
|
|
29
|
+
MANIFEST_NOT_FOUND: (file: string): string =>
|
|
30
|
+
`WebApp manifest not found: ${file}. This file is required for WebApp projects.`,
|
|
31
|
+
|
|
32
|
+
PROJECT_JSON_FOUND:
|
|
33
|
+
"project.json found in WebApp project. The WebApp tool only supports Coded web apps without project.json.",
|
|
34
|
+
|
|
35
|
+
MANIFEST_LOAD_FAILED: (file: string): string =>
|
|
36
|
+
`Failed to load or parse ${file}`,
|
|
37
|
+
|
|
38
|
+
BUNDLE_NOT_FOUND: (path: string): string =>
|
|
39
|
+
`Compiled bundle not found at ${path}. Ensure the project is built before packing.`,
|
|
40
|
+
|
|
41
|
+
BUNDLE_NOT_DIRECTORY: (path: string): string =>
|
|
42
|
+
`Bundle path ${path} exists but is not a directory.`,
|
|
43
|
+
|
|
44
|
+
BUILD_NOT_SUPPORTED:
|
|
45
|
+
"Build execution (isCompiled=false) is not yet supported. Please build the project manually and set isCompiled=true.",
|
|
46
|
+
|
|
47
|
+
VALIDATION_FAILED: (context: string): string =>
|
|
48
|
+
`Validation failed: ${context}`,
|
|
49
|
+
|
|
50
|
+
PACKING_FAILED: (context: string): string =>
|
|
51
|
+
`An error occurred while packing WebApp project: ${context}`,
|
|
52
|
+
|
|
53
|
+
PACKAGE_NAME_REQUIRED: "Package name is required",
|
|
54
|
+
|
|
55
|
+
PACKAGE_VERSION_REQUIRED: "Package version is required",
|
|
56
|
+
|
|
57
|
+
PROJECT_PATH_REQUIRED: "Project path is required",
|
|
58
|
+
|
|
59
|
+
OUTPUT_PATH_REQUIRED: "Output path is required",
|
|
60
|
+
} as const;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { toolsFactoryRepository } from "@uipath/solutionpackager-tool-core";
|
|
2
|
+
import { WebAppToolFactory } from "./webapp-tool-factory.js";
|
|
3
|
+
|
|
4
|
+
export { WebAppTool } from "./webapp-tool.js";
|
|
5
|
+
export { WebAppToolFactory } from "./webapp-tool-factory.js";
|
|
6
|
+
|
|
7
|
+
toolsFactoryRepository.registerProjectToolFactory(new WebAppToolFactory());
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export enum WebAppVariantType {
|
|
2
|
+
Coded = "Coded",
|
|
3
|
+
JS = "JS",
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/** @public */
|
|
7
|
+
export enum AppSubType {
|
|
8
|
+
/** Coded app subtype that is registered with Solutions */
|
|
9
|
+
Coded = "Coded",
|
|
10
|
+
/** CodedAction app subtype that is registered with Solutions */
|
|
11
|
+
CodedAction = "CodedAction",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Variant-specific configurations
|
|
16
|
+
*/
|
|
17
|
+
export interface CodedAppConfig {
|
|
18
|
+
/** If true: pack only; If false: build → pack (future) */
|
|
19
|
+
isCompiled?: boolean;
|
|
20
|
+
/** Compiled bundle folder location (default: dist/) */
|
|
21
|
+
bundlePath?: string;
|
|
22
|
+
/** Optional build command for future scenarios */
|
|
23
|
+
buildCommand?: string;
|
|
24
|
+
isActionApp?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface JsConfig {
|
|
28
|
+
// Placeholder for future properties specific to JS variant
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Map each variant to its specific config type */
|
|
33
|
+
type ManifestConfigMap = {
|
|
34
|
+
[WebAppVariantType.Coded]: CodedAppConfig;
|
|
35
|
+
[WebAppVariantType.JS]: JsConfig;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** WebApp manifest type parameterized by variant */
|
|
39
|
+
export type WebAppManifest<T extends WebAppVariantType = WebAppVariantType> = {
|
|
40
|
+
type: T;
|
|
41
|
+
solutionResourceSubType: string;
|
|
42
|
+
config?: ManifestConfigMap[T];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const WEBAPP_MANIFEST_FILE_NAME = "webAppManifest.json";
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IFileSystem,
|
|
3
|
+
OperateFileModel,
|
|
4
|
+
PackageDescriptorFileModel,
|
|
5
|
+
} from "@uipath/solutionpackager-tool-core";
|
|
6
|
+
import {
|
|
7
|
+
NugetConstants,
|
|
8
|
+
NugetPackager,
|
|
9
|
+
Path,
|
|
10
|
+
ProjectTypes,
|
|
11
|
+
TargetFramework,
|
|
12
|
+
} from "@uipath/solutionpackager-tool-core";
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_BUNDLE_PATH,
|
|
15
|
+
DEFAULT_ENTRY_POINT_TYPE,
|
|
16
|
+
ERROR_MESSAGES,
|
|
17
|
+
TARGET_RUNTIME,
|
|
18
|
+
} from "../constants.js";
|
|
19
|
+
import type { WebAppManifest } from "../models/webapp-manifest.js";
|
|
20
|
+
import {
|
|
21
|
+
WEBAPP_MANIFEST_FILE_NAME,
|
|
22
|
+
type WebAppVariantType,
|
|
23
|
+
} from "../models/webapp-manifest.js";
|
|
24
|
+
import { copyDirectoryAsync } from "../utils/fs-helpers.js";
|
|
25
|
+
import type { PackageArgs, VariantStrategy } from "./variant-strategy.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Coded app strategy for Coded variant
|
|
29
|
+
* Supports isCompiled=true (v1) and isCompiled=false (future)
|
|
30
|
+
*/
|
|
31
|
+
export class CodedAppStrategy implements VariantStrategy {
|
|
32
|
+
constructor(private readonly fileSystem: IFileSystem) {}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Variant-specific validation: warn if compiled bundle is expected but missing
|
|
36
|
+
*/
|
|
37
|
+
async validateAsync(args: {
|
|
38
|
+
fileSystem: IFileSystem;
|
|
39
|
+
projectPath: string;
|
|
40
|
+
manifest: WebAppManifest;
|
|
41
|
+
logger?: {
|
|
42
|
+
info: (message: string) => void;
|
|
43
|
+
error: (message: string) => void;
|
|
44
|
+
progress: (message: string) => void;
|
|
45
|
+
};
|
|
46
|
+
}): Promise<void> {
|
|
47
|
+
const { projectPath, manifest, logger } = args;
|
|
48
|
+
|
|
49
|
+
// Narrow manifest to Coded to read typed config safely
|
|
50
|
+
const typed = manifest as WebAppManifest<WebAppVariantType.Coded>;
|
|
51
|
+
let bundlePath = typed.config?.bundlePath;
|
|
52
|
+
if (typeof bundlePath !== "string" || bundlePath.length === 0) {
|
|
53
|
+
bundlePath = DEFAULT_BUNDLE_PATH;
|
|
54
|
+
}
|
|
55
|
+
const isCompiled = typed.config?.isCompiled ?? true;
|
|
56
|
+
|
|
57
|
+
if (isCompiled) {
|
|
58
|
+
const fullBundlePath = Path.join(projectPath, bundlePath);
|
|
59
|
+
const exists = await this.fileSystem.exists(fullBundlePath);
|
|
60
|
+
if (!exists) {
|
|
61
|
+
const message = ERROR_MESSAGES.BUNDLE_NOT_FOUND(fullBundlePath);
|
|
62
|
+
|
|
63
|
+
// Prefer `warn` if available on the provided logger, otherwise fall back to `info`.
|
|
64
|
+
if ((logger as any)?.warn) {
|
|
65
|
+
(logger as any).warn(message);
|
|
66
|
+
} else {
|
|
67
|
+
logger?.info(`Warning: ${message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async packageAsync(args: PackageArgs): Promise<string> {
|
|
74
|
+
const { projectPath, manifest, outputPath, packageInfo, logger } = args;
|
|
75
|
+
|
|
76
|
+
logger?.info(
|
|
77
|
+
`Packaging Coded variant: ${packageInfo.id}@${packageInfo.version}`,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Get bundle path from manifest or use default
|
|
81
|
+
let bundlePath = (manifest as WebAppManifest<WebAppVariantType.Coded>)
|
|
82
|
+
.config?.bundlePath;
|
|
83
|
+
if (typeof bundlePath !== "string" || bundlePath.length === 0) {
|
|
84
|
+
bundlePath = DEFAULT_BUNDLE_PATH;
|
|
85
|
+
}
|
|
86
|
+
const isCompiled =
|
|
87
|
+
(manifest as WebAppManifest<WebAppVariantType.Coded>).config
|
|
88
|
+
?.isCompiled ?? true;
|
|
89
|
+
|
|
90
|
+
const fullBundlePath = Path.join(projectPath, bundlePath);
|
|
91
|
+
|
|
92
|
+
// Validate compiled bundle exists (for v1, isCompiled=true)
|
|
93
|
+
if (isCompiled) {
|
|
94
|
+
logger?.progress("Validating compiled bundle...");
|
|
95
|
+
const bundleExists = await this.fileSystem.exists(fullBundlePath);
|
|
96
|
+
if (!bundleExists) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
ERROR_MESSAGES.BUNDLE_NOT_FOUND(fullBundlePath),
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const bundleStat = await this.fileSystem.stat(fullBundlePath);
|
|
103
|
+
if (!bundleStat?.isDirectory()) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
ERROR_MESSAGES.BUNDLE_NOT_DIRECTORY(fullBundlePath),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
// Future: Run build command
|
|
110
|
+
logger?.info(
|
|
111
|
+
"Build mode (isCompiled=false) is not yet implemented in v1.",
|
|
112
|
+
);
|
|
113
|
+
throw new Error(ERROR_MESSAGES.BUILD_NOT_SUPPORTED);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Create bundle directory structure (similar to api-workflow)
|
|
117
|
+
const localBuildFolder = Path.join(
|
|
118
|
+
outputPath,
|
|
119
|
+
NugetConstants.OutputFolderName,
|
|
120
|
+
);
|
|
121
|
+
const contentFolder = Path.join(
|
|
122
|
+
localBuildFolder,
|
|
123
|
+
NugetConstants.ContentFolderName,
|
|
124
|
+
);
|
|
125
|
+
await this.fileSystem.mkdir(contentFolder);
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
// Copy bundle to content folder
|
|
129
|
+
logger?.progress("Copying bundle to content folder...");
|
|
130
|
+
await copyDirectoryAsync(
|
|
131
|
+
this.fileSystem,
|
|
132
|
+
fullBundlePath,
|
|
133
|
+
contentFolder,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Prepare metadata files
|
|
137
|
+
logger?.progress("Preparing metadata files...");
|
|
138
|
+
await this.prepareMetadataFiles(
|
|
139
|
+
localBuildFolder,
|
|
140
|
+
contentFolder,
|
|
141
|
+
packageInfo,
|
|
142
|
+
manifest,
|
|
143
|
+
projectPath,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Create the NuGet package using native packager
|
|
147
|
+
logger?.progress("Creating NuGet package...");
|
|
148
|
+
const nupkgFileName = `${packageInfo.id}.${packageInfo.version}.nupkg`;
|
|
149
|
+
const nupkgPath = Path.join(outputPath, nupkgFileName);
|
|
150
|
+
|
|
151
|
+
const packager = new NugetPackager(this.fileSystem);
|
|
152
|
+
const result = await packager.packAsync(
|
|
153
|
+
localBuildFolder,
|
|
154
|
+
packageInfo,
|
|
155
|
+
nupkgPath,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
logger?.info(`Package created successfully: ${result.outputPath}`);
|
|
159
|
+
return result.outputPath;
|
|
160
|
+
} finally {
|
|
161
|
+
// Always cleanup the temporary build folder
|
|
162
|
+
try {
|
|
163
|
+
await this.fileSystem.rm(localBuildFolder);
|
|
164
|
+
} catch (cleanupError) {
|
|
165
|
+
logger?.error(
|
|
166
|
+
`Failed to cleanup build folder: ${
|
|
167
|
+
cleanupError instanceof Error
|
|
168
|
+
? cleanupError.message
|
|
169
|
+
: String(cleanupError)
|
|
170
|
+
}`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Prepare metadata files (operate.json, package-descriptor.json, bindings.json, bindings_v2.json, entry-points.json)
|
|
178
|
+
*/
|
|
179
|
+
private async prepareMetadataFiles(
|
|
180
|
+
_localBuildFolder: string,
|
|
181
|
+
contentFolder: string,
|
|
182
|
+
packageInfo: {
|
|
183
|
+
id: string;
|
|
184
|
+
version: string;
|
|
185
|
+
author?: string;
|
|
186
|
+
description?: string;
|
|
187
|
+
},
|
|
188
|
+
_manifest: any,
|
|
189
|
+
projectPath: string,
|
|
190
|
+
): Promise<void> {
|
|
191
|
+
// Determine main file - check for index.html or use default
|
|
192
|
+
let mainFile = "index.html";
|
|
193
|
+
const indexHtmlPath = Path.join(contentFolder, "index.html");
|
|
194
|
+
const indexHtmlExists = await this.fileSystem.exists(indexHtmlPath);
|
|
195
|
+
if (!indexHtmlExists) {
|
|
196
|
+
// Check for other common entry points
|
|
197
|
+
const possibleEntries = ["index.html", "main.html", "app.html"];
|
|
198
|
+
for (const entry of possibleEntries) {
|
|
199
|
+
const entryPath = Path.join(contentFolder, entry);
|
|
200
|
+
if (await this.fileSystem.exists(entryPath)) {
|
|
201
|
+
mainFile = entry;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Create operate.json in content folder
|
|
208
|
+
const operatePath = Path.join(
|
|
209
|
+
contentFolder,
|
|
210
|
+
NugetConstants.OperateFileName,
|
|
211
|
+
);
|
|
212
|
+
const operateModel: OperateFileModel & { targetRuntime: string } = {
|
|
213
|
+
projectId: packageInfo.id,
|
|
214
|
+
main: mainFile,
|
|
215
|
+
contentType: ProjectTypes.WebApp,
|
|
216
|
+
targetFramework: TargetFramework.Portable,
|
|
217
|
+
targetRuntime: TARGET_RUNTIME,
|
|
218
|
+
runtimeOptions: {
|
|
219
|
+
requiresUserInteraction: false,
|
|
220
|
+
isAttended: false,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const operateJson = JSON.stringify(operateModel, null, 2);
|
|
225
|
+
await this.fileSystem.writeFile(operatePath, operateJson);
|
|
226
|
+
|
|
227
|
+
// Copy webAppManifest.json from project root into content (as-is)
|
|
228
|
+
const manifestSourcePath = Path.join(
|
|
229
|
+
projectPath,
|
|
230
|
+
WEBAPP_MANIFEST_FILE_NAME,
|
|
231
|
+
);
|
|
232
|
+
const manifestDestPath = Path.join(
|
|
233
|
+
contentFolder,
|
|
234
|
+
WEBAPP_MANIFEST_FILE_NAME,
|
|
235
|
+
);
|
|
236
|
+
const manifestExists = await this.fileSystem.exists(manifestSourcePath);
|
|
237
|
+
if (manifestExists) {
|
|
238
|
+
const manifestContent =
|
|
239
|
+
await this.fileSystem.readFile(manifestSourcePath);
|
|
240
|
+
if (manifestContent) {
|
|
241
|
+
await this.fileSystem.writeFile(
|
|
242
|
+
manifestDestPath,
|
|
243
|
+
manifestContent,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Copy uipath.json from project root into content (if it exists)
|
|
249
|
+
const uipathJsonSourcePath = Path.join(projectPath, "uipath.json");
|
|
250
|
+
const uipathJsonDestPath = Path.join(contentFolder, "uipath.json");
|
|
251
|
+
const uipathJsonExists =
|
|
252
|
+
await this.fileSystem.exists(uipathJsonSourcePath);
|
|
253
|
+
if (uipathJsonExists) {
|
|
254
|
+
const uipathJsonContent =
|
|
255
|
+
await this.fileSystem.readFile(uipathJsonSourcePath);
|
|
256
|
+
if (uipathJsonContent) {
|
|
257
|
+
await this.fileSystem.writeFile(
|
|
258
|
+
uipathJsonDestPath,
|
|
259
|
+
uipathJsonContent,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Create or copy bindings.json (version 1.0)
|
|
265
|
+
const bindingsPath = Path.join(contentFolder, "bindings.json");
|
|
266
|
+
const bindingsExists = await this.fileSystem.exists(bindingsPath);
|
|
267
|
+
|
|
268
|
+
if (!bindingsExists) {
|
|
269
|
+
const bindingsJson = JSON.stringify(
|
|
270
|
+
{
|
|
271
|
+
version: "1.0",
|
|
272
|
+
resources: [],
|
|
273
|
+
},
|
|
274
|
+
null,
|
|
275
|
+
2,
|
|
276
|
+
);
|
|
277
|
+
await this.fileSystem.writeFile(bindingsPath, bindingsJson);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Create or copy bindings_v2.json (version 2.0)
|
|
281
|
+
const bindingsV2Path = Path.join(contentFolder, "bindings_v2.json");
|
|
282
|
+
const bindingsV2Exists = await this.fileSystem.exists(bindingsV2Path);
|
|
283
|
+
|
|
284
|
+
if (!bindingsV2Exists) {
|
|
285
|
+
const bindingsV2Json = JSON.stringify(
|
|
286
|
+
{
|
|
287
|
+
version: "2.0",
|
|
288
|
+
resources: [],
|
|
289
|
+
},
|
|
290
|
+
null,
|
|
291
|
+
2,
|
|
292
|
+
);
|
|
293
|
+
await this.fileSystem.writeFile(bindingsV2Path, bindingsV2Json);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Create or copy entry-points.json
|
|
297
|
+
const entryPointsPath = Path.join(contentFolder, "entry-points.json");
|
|
298
|
+
const projectEntryPointsPath = Path.join(
|
|
299
|
+
projectPath,
|
|
300
|
+
"entry-points.json",
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
let entryPointsContent: string;
|
|
304
|
+
const projectEntryPointsExists = await this.fileSystem.exists(
|
|
305
|
+
projectEntryPointsPath,
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
if (projectEntryPointsExists) {
|
|
309
|
+
const entryPointsData = await this.fileSystem.readFile(
|
|
310
|
+
projectEntryPointsPath,
|
|
311
|
+
);
|
|
312
|
+
if (entryPointsData) {
|
|
313
|
+
entryPointsContent = new TextDecoder().decode(entryPointsData);
|
|
314
|
+
} else {
|
|
315
|
+
entryPointsContent = this.createDefaultEntryPoints(mainFile);
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
entryPointsContent = this.createDefaultEntryPoints(mainFile);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
await this.fileSystem.writeFile(entryPointsPath, entryPointsContent);
|
|
322
|
+
|
|
323
|
+
// Create package-descriptor.json in content folder
|
|
324
|
+
const packageDescriptorPath = Path.join(
|
|
325
|
+
contentFolder,
|
|
326
|
+
NugetConstants.PackageDescriptorFileName,
|
|
327
|
+
);
|
|
328
|
+
const packageDescriptor: PackageDescriptorFileModel = {
|
|
329
|
+
files: {
|
|
330
|
+
[NugetConstants.OperateFileName]: Path.join(
|
|
331
|
+
NugetConstants.ContentFolderName,
|
|
332
|
+
NugetConstants.OperateFileName,
|
|
333
|
+
),
|
|
334
|
+
"entry-points.json": Path.join(
|
|
335
|
+
NugetConstants.ContentFolderName,
|
|
336
|
+
"entry-points.json",
|
|
337
|
+
),
|
|
338
|
+
"bindings.json": Path.join(
|
|
339
|
+
NugetConstants.ContentFolderName,
|
|
340
|
+
"bindings_v2.json",
|
|
341
|
+
),
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const packageDescriptorJson = JSON.stringify(
|
|
346
|
+
packageDescriptor,
|
|
347
|
+
null,
|
|
348
|
+
2,
|
|
349
|
+
);
|
|
350
|
+
await this.fileSystem.writeFile(
|
|
351
|
+
packageDescriptorPath,
|
|
352
|
+
packageDescriptorJson,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Create default entry-points.json structure
|
|
358
|
+
*/
|
|
359
|
+
private createDefaultEntryPoints(mainFile: string): string {
|
|
360
|
+
const uniqueId = this.generateUniqueId();
|
|
361
|
+
|
|
362
|
+
const entryPoints = {
|
|
363
|
+
$schema: "https://cloud.uipath.com/draft/2024-12/entry-point",
|
|
364
|
+
$id: "entry-points-doc-001",
|
|
365
|
+
entryPoints: [
|
|
366
|
+
{
|
|
367
|
+
filePath: mainFile,
|
|
368
|
+
uniqueId: uniqueId,
|
|
369
|
+
type: DEFAULT_ENTRY_POINT_TYPE,
|
|
370
|
+
input: {
|
|
371
|
+
amount: { type: "integer" },
|
|
372
|
+
id: { type: "string" },
|
|
373
|
+
},
|
|
374
|
+
output: {
|
|
375
|
+
status: { type: "string" },
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
return JSON.stringify(entryPoints, null, 2);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Generate a cryptographically secure unique ID (UUID v4-like format)
|
|
386
|
+
*/
|
|
387
|
+
private generateUniqueId(): string {
|
|
388
|
+
const randomBytes = new Uint8Array(16);
|
|
389
|
+
|
|
390
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
391
|
+
crypto.getRandomValues(randomBytes);
|
|
392
|
+
} else {
|
|
393
|
+
throw new Error("crypto.getRandomValues is not available");
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Set version (4) and variant bits according to UUID v4 spec
|
|
397
|
+
randomBytes[6] = (randomBytes[6] & 0x0f) | 0x40; // Version 4
|
|
398
|
+
randomBytes[8] = (randomBytes[8] & 0x3f) | 0x80; // Variant 10
|
|
399
|
+
|
|
400
|
+
// Convert to UUID string format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
|
401
|
+
const hex = Array.from(randomBytes)
|
|
402
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
403
|
+
.join("");
|
|
404
|
+
|
|
405
|
+
return [
|
|
406
|
+
hex.substring(0, 8),
|
|
407
|
+
hex.substring(8, 12),
|
|
408
|
+
hex.substring(12, 16),
|
|
409
|
+
hex.substring(16, 20),
|
|
410
|
+
hex.substring(20, 32),
|
|
411
|
+
].join("-");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IFileSystem } from "@uipath/solutionpackager-tool-core";
|
|
2
|
+
import type { WebAppManifest } from "../models/webapp-manifest.js";
|
|
3
|
+
import { WebAppVariantType } from "../models/webapp-manifest.js";
|
|
4
|
+
import { CodedAppStrategy } from "./coded-app-strategy.js";
|
|
5
|
+
import type { VariantStrategy } from "./variant-strategy.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Factory for creating variant-specific packaging strategies
|
|
9
|
+
*/
|
|
10
|
+
export class VariantStrategyFactory {
|
|
11
|
+
/**
|
|
12
|
+
* Create a strategy instance based on manifest type
|
|
13
|
+
* @param manifest - WebApp manifest
|
|
14
|
+
* @param fileSystem - File system instance
|
|
15
|
+
* @returns Strategy instance
|
|
16
|
+
* @throws Error if variant is not supported
|
|
17
|
+
*/
|
|
18
|
+
static createStrategy(
|
|
19
|
+
manifest: WebAppManifest,
|
|
20
|
+
fileSystem: IFileSystem,
|
|
21
|
+
): VariantStrategy {
|
|
22
|
+
const variant = manifest.type;
|
|
23
|
+
|
|
24
|
+
switch (variant) {
|
|
25
|
+
case WebAppVariantType.Coded:
|
|
26
|
+
return new CodedAppStrategy(fileSystem);
|
|
27
|
+
|
|
28
|
+
case WebAppVariantType.JS:
|
|
29
|
+
throw new Error(
|
|
30
|
+
"JS variant is not yet implemented. Only Coded is supported in v1.",
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
default:
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Unknown WebApp variant: ${manifest.type}. Supported variants: ${Object.values(WebAppVariantType).join(", ")}`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|