@uipath/packager-tool-bpmn 1.202.0-preview.140 → 1.202.0-preview.142
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/package.json +4 -8
- package/src/bpmn-tool-factory.ts +0 -26
- package/src/bpmn-tool.ts +0 -632
- package/src/index.ts +0 -13
package/package.json
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/packager-tool-bpmn",
|
|
3
|
-
"version": "1.202.0-preview.
|
|
3
|
+
"version": "1.202.0-preview.142",
|
|
4
4
|
"description": "UiPath ProcessOrchestration (BPMN) tool implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
|
-
".":
|
|
8
|
-
"source": "./src/index.ts",
|
|
9
|
-
"default": "./dist/index.js"
|
|
10
|
-
}
|
|
7
|
+
".": "./dist/index.js"
|
|
11
8
|
},
|
|
12
9
|
"repository": {
|
|
13
10
|
"type": "git",
|
|
@@ -19,8 +16,7 @@
|
|
|
19
16
|
},
|
|
20
17
|
"types": "./dist/index.d.ts",
|
|
21
18
|
"files": [
|
|
22
|
-
"dist"
|
|
23
|
-
"src"
|
|
19
|
+
"dist"
|
|
24
20
|
],
|
|
25
21
|
"author": "",
|
|
26
22
|
"license": "ISC",
|
|
@@ -30,5 +26,5 @@
|
|
|
30
26
|
"dependencies": {
|
|
31
27
|
"@uipath/maestro-sdk": "1.202.0"
|
|
32
28
|
},
|
|
33
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "2f7204440851d62ecc9f2d2e202c863293821d90"
|
|
34
30
|
}
|
package/src/bpmn-tool-factory.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type IFileSystem,
|
|
3
|
-
type IProjectToolFactory,
|
|
4
|
-
type IToolLogger,
|
|
5
|
-
type ProjectTool,
|
|
6
|
-
type ProjectType,
|
|
7
|
-
ProjectTypes,
|
|
8
|
-
} from "@uipath/solutionpackager-tool-core";
|
|
9
|
-
import { BpmnTool } from "./bpmn-tool.js";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Factory for creating ProcessOrchestration (BPMN) project tools
|
|
13
|
-
*/
|
|
14
|
-
export class BpmnToolFactory implements IProjectToolFactory {
|
|
15
|
-
readonly supportedTypes: readonly ProjectType[] = [
|
|
16
|
-
ProjectTypes.ProcessOrchestration,
|
|
17
|
-
"processOrchestration",
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
async createAsync(
|
|
21
|
-
logger: IToolLogger,
|
|
22
|
-
fileSystem: IFileSystem,
|
|
23
|
-
): Promise<ProjectTool> {
|
|
24
|
-
return new BpmnTool(fileSystem, logger);
|
|
25
|
-
}
|
|
26
|
-
}
|
package/src/bpmn-tool.ts
DELETED
|
@@ -1,632 +0,0 @@
|
|
|
1
|
-
// Type-only: erases at build, so it does not put the module on the import path.
|
|
2
|
-
import type {
|
|
3
|
-
BpmnPackageFile,
|
|
4
|
-
JsonObject,
|
|
5
|
-
} from "@uipath/maestro-sdk/bpmn-packaging";
|
|
6
|
-
import type { BpmnValidationFileSystem } from "@uipath/maestro-sdk/bpmn-validation";
|
|
7
|
-
import { BpmnValidateService } from "@uipath/maestro-sdk/bpmn-validation";
|
|
8
|
-
import type {
|
|
9
|
-
IProjectBuildOptions,
|
|
10
|
-
IProjectPackOptions,
|
|
11
|
-
IProjectRestoreOptions,
|
|
12
|
-
IProjectValidateOptions,
|
|
13
|
-
UiProject,
|
|
14
|
-
} from "@uipath/solutionpackager-tool-core";
|
|
15
|
-
import {
|
|
16
|
-
ensureContentOperateFile,
|
|
17
|
-
type IFileSystem,
|
|
18
|
-
type IToolLogger,
|
|
19
|
-
NugetConstants,
|
|
20
|
-
NugetPackager,
|
|
21
|
-
Path,
|
|
22
|
-
ProjectTool,
|
|
23
|
-
ProjectTypes,
|
|
24
|
-
TemporaryStorageService,
|
|
25
|
-
ToolErrorCodes,
|
|
26
|
-
ToolResult,
|
|
27
|
-
} from "@uipath/solutionpackager-tool-core";
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* BPMN-specific file names (same conventions as Flow)
|
|
31
|
-
* ProcessOrchestration projects use 'entry-points.json' (with dash) and 'bindings_v2.json' (with underscore)
|
|
32
|
-
*/
|
|
33
|
-
const BpmnConstants = {
|
|
34
|
-
EntryPointsFileName: "entry-points.json",
|
|
35
|
-
BindingsV2FileName: "bindings_v2.json",
|
|
36
|
-
} as const;
|
|
37
|
-
|
|
38
|
-
function createBpmnValidationFileSystem(
|
|
39
|
-
fileSystem: IFileSystem,
|
|
40
|
-
): BpmnValidationFileSystem {
|
|
41
|
-
return {
|
|
42
|
-
readdir: (path) => fileSystem.readdir(path),
|
|
43
|
-
readFile: (path) => fileSystem.readFile(path),
|
|
44
|
-
exists: (path) => fileSystem.exists(path),
|
|
45
|
-
joinPath: (...segments) => Path.join(...segments),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* ProcessOrchestration (BPMN) project tool implementation
|
|
51
|
-
*/
|
|
52
|
-
export class BpmnTool extends ProjectTool {
|
|
53
|
-
private readonly _temporaryStorage: TemporaryStorageService;
|
|
54
|
-
// Standalone `validate` keeps an extension type absent from the contract a
|
|
55
|
-
// warning, so authoring against a type newer than the CLI's bundled contract
|
|
56
|
-
// is not blocked.
|
|
57
|
-
private readonly _validateService: BpmnValidateService;
|
|
58
|
-
// Packaging must fail fast on such a type rather than ship a package
|
|
59
|
-
// Orchestrator rejects at deploy (Error 1654), so the build path validates
|
|
60
|
-
// with error severity.
|
|
61
|
-
private readonly _packValidateService: BpmnValidateService;
|
|
62
|
-
|
|
63
|
-
constructor(fileSystem: IFileSystem, logger: IToolLogger) {
|
|
64
|
-
super(fileSystem, logger);
|
|
65
|
-
this._temporaryStorage = new TemporaryStorageService(fileSystem);
|
|
66
|
-
const validationFileSystem = createBpmnValidationFileSystem(fileSystem);
|
|
67
|
-
this._validateService = new BpmnValidateService(validationFileSystem);
|
|
68
|
-
this._packValidateService = new BpmnValidateService(
|
|
69
|
-
validationFileSystem,
|
|
70
|
-
{ unsupportedExtensionTypeSeverity: "error" },
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
override async restoreAsync(
|
|
75
|
-
_options: IProjectRestoreOptions,
|
|
76
|
-
_cancellationToken?: AbortSignal,
|
|
77
|
-
): Promise<ToolResult> {
|
|
78
|
-
this.logger.info(
|
|
79
|
-
"Restore operation is not required for ProcessOrchestration projects",
|
|
80
|
-
);
|
|
81
|
-
return ToolResult.success();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
override async validateAsync(
|
|
85
|
-
options: IProjectValidateOptions,
|
|
86
|
-
_cancellationToken?: AbortSignal,
|
|
87
|
-
): Promise<ToolResult> {
|
|
88
|
-
return this.runValidation(this._validateService, options.projectPath);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private async runValidation(
|
|
92
|
-
service: BpmnValidateService,
|
|
93
|
-
projectPath: string,
|
|
94
|
-
): Promise<ToolResult> {
|
|
95
|
-
this.logger.info("Validating ProcessOrchestration BPMN project...");
|
|
96
|
-
const result = await service.validateProject(projectPath);
|
|
97
|
-
if (!result.isValid) {
|
|
98
|
-
if (result.message) {
|
|
99
|
-
this.logger.error(result.message);
|
|
100
|
-
}
|
|
101
|
-
return ToolResult.error(
|
|
102
|
-
result.errorCode ?? ToolErrorCodes.InternalError,
|
|
103
|
-
result.message,
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
if (result.message) {
|
|
107
|
-
this.logger.warn(result.message);
|
|
108
|
-
}
|
|
109
|
-
this.logger.info("ProcessOrchestration BPMN validation completed");
|
|
110
|
-
return ToolResult.success();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
override async buildAsync(
|
|
114
|
-
options: IProjectBuildOptions,
|
|
115
|
-
_cancellationToken?: AbortSignal,
|
|
116
|
-
): Promise<ToolResult> {
|
|
117
|
-
const tempFolder = await this._temporaryStorage.getTempFolderPath();
|
|
118
|
-
const localBuildFolder = Path.join(
|
|
119
|
-
tempFolder,
|
|
120
|
-
NugetConstants.OutputFolderName,
|
|
121
|
-
);
|
|
122
|
-
const contentFolder = Path.join(
|
|
123
|
-
localBuildFolder,
|
|
124
|
-
NugetConstants.ContentFolderName,
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
if (!options.skipValidate) {
|
|
129
|
-
// Error severity here: the build/pack path fails fast on an
|
|
130
|
-
// extension type absent from the contract, unlike standalone
|
|
131
|
-
// `validate`.
|
|
132
|
-
const validationResult = await this.runValidation(
|
|
133
|
-
this._packValidateService,
|
|
134
|
-
options.projectPath,
|
|
135
|
-
);
|
|
136
|
-
if (!validationResult.isSuccess) {
|
|
137
|
-
return validationResult;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
this.logger.progress("Copying files...");
|
|
142
|
-
await this.copyFiles(options.projectPath, contentFolder);
|
|
143
|
-
|
|
144
|
-
// Before operate.json: `ensureContentOperateFile` derives `main`
|
|
145
|
-
// from entry-points.json, so a project that shipped without one
|
|
146
|
-
// used to get `main: ""`.
|
|
147
|
-
this.logger.progress(
|
|
148
|
-
"Deriving packaging artifacts from the BPMN model...",
|
|
149
|
-
);
|
|
150
|
-
const artifactResult = await this.generateBpmnPackagingArtifacts(
|
|
151
|
-
contentFolder,
|
|
152
|
-
options,
|
|
153
|
-
);
|
|
154
|
-
if (artifactResult) {
|
|
155
|
-
return artifactResult;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
this.logger.progress("Creating operate.json file...");
|
|
159
|
-
await this.createOperateFile(options, contentFolder);
|
|
160
|
-
|
|
161
|
-
this.logger.progress("Creating package-descriptor.json file...");
|
|
162
|
-
await this.createPackageDescriptor(localBuildFolder, contentFolder);
|
|
163
|
-
|
|
164
|
-
return new ToolResult(ToolErrorCodes.Success, "done", [
|
|
165
|
-
localBuildFolder,
|
|
166
|
-
]);
|
|
167
|
-
} catch (error) {
|
|
168
|
-
const errorMessage =
|
|
169
|
-
error instanceof Error ? error.toString() : String(error);
|
|
170
|
-
this.logger.error(errorMessage);
|
|
171
|
-
return ToolResult.error(
|
|
172
|
-
ToolErrorCodes.InternalError,
|
|
173
|
-
"An error occurred while building ProcessOrchestration project files",
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
override async packAsync(
|
|
179
|
-
options: IProjectPackOptions,
|
|
180
|
-
cancellationToken?: AbortSignal,
|
|
181
|
-
): Promise<ToolResult> {
|
|
182
|
-
// First, run the build step to prepare the bundle content
|
|
183
|
-
const buildResult = await this.buildAsync(options, cancellationToken);
|
|
184
|
-
if (!buildResult.isSuccess) {
|
|
185
|
-
return buildResult;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const localBuildFolder = buildResult.packages[0];
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
// Create the NuGet package
|
|
192
|
-
this.logger.progress("Creating NuGet package...");
|
|
193
|
-
|
|
194
|
-
const nupkgFileName = `${options.package.id}.${options.package.version}.nupkg`;
|
|
195
|
-
const nupkgPath = Path.join(options.outputPath, nupkgFileName);
|
|
196
|
-
|
|
197
|
-
const packager = new NugetPackager(this.fileSystem);
|
|
198
|
-
const result = await packager.packAsync(
|
|
199
|
-
localBuildFolder,
|
|
200
|
-
options.package,
|
|
201
|
-
nupkgPath,
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
this.logger.progress("Package created successfully");
|
|
205
|
-
return new ToolResult(ToolErrorCodes.Success, "done", [
|
|
206
|
-
result.outputPath,
|
|
207
|
-
]);
|
|
208
|
-
} catch (error) {
|
|
209
|
-
const errorMessage =
|
|
210
|
-
error instanceof Error ? error.toString() : String(error);
|
|
211
|
-
this.logger.error(errorMessage);
|
|
212
|
-
return ToolResult.error(
|
|
213
|
-
ToolErrorCodes.InternalError,
|
|
214
|
-
"An error occurred while packing ProcessOrchestration project",
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
override async dispose(): Promise<void> {
|
|
220
|
-
this.logger.info("Disposing ProcessOrchestration Tool");
|
|
221
|
-
try {
|
|
222
|
-
await this._temporaryStorage.cleanup();
|
|
223
|
-
} catch {
|
|
224
|
-
// Ignore errors during cleanup
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Copy all files from source path to content folder
|
|
230
|
-
*/
|
|
231
|
-
private async copyFiles(
|
|
232
|
-
sourcePath: string,
|
|
233
|
-
destinationPath: string,
|
|
234
|
-
): Promise<void> {
|
|
235
|
-
await this.fileSystem.mkdir(destinationPath);
|
|
236
|
-
|
|
237
|
-
const files = await this.fileSystem.readdir(sourcePath);
|
|
238
|
-
|
|
239
|
-
for (const file of files) {
|
|
240
|
-
const sourceFile = Path.join(sourcePath, file);
|
|
241
|
-
const destinationFile = Path.join(destinationPath, file);
|
|
242
|
-
|
|
243
|
-
const stat = await this.fileSystem.stat(sourceFile);
|
|
244
|
-
if (stat?.isFile()) {
|
|
245
|
-
const content = await this.fileSystem.readFile(sourceFile);
|
|
246
|
-
if (content) {
|
|
247
|
-
await this.fileSystem.writeFile(destinationFile, content);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Regenerate `entry-points.json` and `bindings_v2.json` in the staging
|
|
255
|
-
* content folder from the BPMN model.
|
|
256
|
-
*
|
|
257
|
-
* `package-descriptor.json` declares both files unconditionally, but they
|
|
258
|
-
* only reached the package if the project source already carried them —
|
|
259
|
-
* which depends on who authored it. A Studio Web `.uis` export omits both
|
|
260
|
-
* for ProcessOrchestration, so those packages were rejected by the Solution
|
|
261
|
-
* Feed every time (`1203`, or `1205` for the missing entry point) while
|
|
262
|
-
* `uip solution pack` still reported success (cli#3299).
|
|
263
|
-
*
|
|
264
|
-
* Regenerating unconditionally — rather than only filling gaps — matches
|
|
265
|
-
* `packager-tool-flow` and `uip maestro refresh`, and is safe here because
|
|
266
|
-
* entry-point identity is read from the BPMN and never minted:
|
|
267
|
-
* `<bpmn:startEvent id>` supplies the filePath fragment and
|
|
268
|
-
* `<uipath:entryPointId value>` the `uniqueId`. So a stale copy on disk
|
|
269
|
-
* cannot ship, and repacking the same source is idempotent. Author-owned
|
|
270
|
-
* detail survives: `buildBpmnPackageMetadata` merges existing JSON-schema
|
|
271
|
-
* annotations, and `buildConnectionResources` preserves resource entries it
|
|
272
|
-
* does not manage.
|
|
273
|
-
*
|
|
274
|
-
* Writes into `contentFolder` (the staging copy), never back into the
|
|
275
|
-
* user's project.
|
|
276
|
-
*
|
|
277
|
-
* Additive by design: when the model yields no entry point the project's own
|
|
278
|
-
* artifacts are left exactly as they were. `validateAsync` accepts a
|
|
279
|
-
* `<bpmn:startEvent>` without a `uipath:entryPointId` (a subprocess-only or
|
|
280
|
-
* event-triggered process is a legitimate shape), so deriving is an
|
|
281
|
-
* improvement where it is possible rather than a new gate — failing here
|
|
282
|
-
* would reject projects that pack today on something validation permits.
|
|
283
|
-
* Declining to derive is not the same as fabricating: an entry-point id is
|
|
284
|
-
* never minted, because a wrong one deploys clean and faults at run time.
|
|
285
|
-
*/
|
|
286
|
-
private async generateBpmnPackagingArtifacts(
|
|
287
|
-
contentFolder: string,
|
|
288
|
-
options: IProjectBuildOptions,
|
|
289
|
-
): Promise<ToolResult | undefined> {
|
|
290
|
-
const reason = await this.deriveBpmnArtifacts(contentFolder, options);
|
|
291
|
-
if (!reason) {
|
|
292
|
-
return undefined;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Could not derive. A package missing either artifact is rejected by the
|
|
296
|
-
// Solution Feed every time — 1203 for unreadable bindings, 1205 for no
|
|
297
|
-
// entry point — so producing one only defers a guaranteed failure to
|
|
298
|
-
// publish time. Fail here instead, with the real cause.
|
|
299
|
-
//
|
|
300
|
-
// But only when they are genuinely absent: a project may legitimately
|
|
301
|
-
// carry artifacts this packager cannot re-derive (authored by Studio
|
|
302
|
-
// Web, or a model shape the derivation does not cover). Those publish
|
|
303
|
-
// fine today, so they are passed through untouched rather than rejected.
|
|
304
|
-
const missing: string[] = [];
|
|
305
|
-
for (const name of [
|
|
306
|
-
BpmnConstants.EntryPointsFileName,
|
|
307
|
-
BpmnConstants.BindingsV2FileName,
|
|
308
|
-
]) {
|
|
309
|
-
if (
|
|
310
|
-
!(await this.fileSystem.exists(Path.join(contentFolder, name)))
|
|
311
|
-
) {
|
|
312
|
-
missing.push(name);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (missing.length === 0) {
|
|
317
|
-
this.logger.warn(
|
|
318
|
-
`Could not derive the packaging artifacts, keeping the project's own: ${reason}`,
|
|
319
|
-
);
|
|
320
|
-
return undefined;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const message =
|
|
324
|
-
`Cannot produce ${missing.join(" and ")} for this ProcessOrchestration project, ` +
|
|
325
|
-
`and the project does not provide ${missing.length === 1 ? "it" : "them"}: ${reason} ` +
|
|
326
|
-
`The Solution Feed rejects a package without these files, so packing would produce ` +
|
|
327
|
-
`something that cannot be published.`;
|
|
328
|
-
this.logger.error(message);
|
|
329
|
-
return ToolResult.error(ToolErrorCodes.InternalError, message);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Derive and write the artifacts. Returns `undefined` on success, or a
|
|
334
|
-
* human-readable reason why derivation was not possible — the caller decides
|
|
335
|
-
* whether that is fatal.
|
|
336
|
-
*/
|
|
337
|
-
private async deriveBpmnArtifacts(
|
|
338
|
-
contentFolder: string,
|
|
339
|
-
options: IProjectBuildOptions,
|
|
340
|
-
): Promise<string | undefined> {
|
|
341
|
-
const selected = await this.selectBpmnFile(contentFolder);
|
|
342
|
-
if (selected.file === undefined) {
|
|
343
|
-
return selected.reason;
|
|
344
|
-
}
|
|
345
|
-
const bpmnFile = selected.file;
|
|
346
|
-
|
|
347
|
-
const raw = await this.fileSystem.readFile(
|
|
348
|
-
Path.join(contentFolder, bpmnFile),
|
|
349
|
-
);
|
|
350
|
-
// `raw.length` matters: read without an encoding, `readFile` returns
|
|
351
|
-
// null only on ENOENT, so a zero-byte file arrives as an empty — but
|
|
352
|
-
// truthy — Uint8Array. A truthiness-only guard would let it fall through
|
|
353
|
-
// to the parser and surface as a generic parse failure instead.
|
|
354
|
-
if (!raw || raw.length === 0) {
|
|
355
|
-
return `${bpmnFile} is empty.`;
|
|
356
|
-
}
|
|
357
|
-
const xml =
|
|
358
|
-
typeof raw === "string" ? raw : new TextDecoder().decode(raw);
|
|
359
|
-
|
|
360
|
-
// Deferred, not a top-level import: the derivation carries bpmn-moddle,
|
|
361
|
-
// and a static import lands it in a chunk that `maestro-tool`'s eager
|
|
362
|
-
// startup graph also touches — it is only needed when a BPMN project is
|
|
363
|
-
// actually built. The BPMN validate service and refresh command defer
|
|
364
|
-
// their own heavy code for the same reason.
|
|
365
|
-
const {
|
|
366
|
-
buildBpmnPackageMetadata,
|
|
367
|
-
buildConnectionResources,
|
|
368
|
-
deriveBpmnEntryPoints,
|
|
369
|
-
formatBpmnPackageFile,
|
|
370
|
-
isJsonObject,
|
|
371
|
-
parseBpmnProcesses,
|
|
372
|
-
parseOptionalJson,
|
|
373
|
-
} = await import("@uipath/maestro-sdk/bpmn-packaging");
|
|
374
|
-
|
|
375
|
-
let processes: Awaited<ReturnType<typeof parseBpmnProcesses>>;
|
|
376
|
-
let entryPoints: ReturnType<typeof deriveBpmnEntryPoints>;
|
|
377
|
-
try {
|
|
378
|
-
processes = await parseBpmnProcesses(xml);
|
|
379
|
-
entryPoints = deriveBpmnEntryPoints(processes);
|
|
380
|
-
} catch (error) {
|
|
381
|
-
const message =
|
|
382
|
-
error instanceof Error ? error.message : String(error);
|
|
383
|
-
return `${bpmnFile} does not yield an entry point — ${message}`;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
const existing = await this.readExistingArtifacts(
|
|
387
|
-
contentFolder,
|
|
388
|
-
parseOptionalJson,
|
|
389
|
-
);
|
|
390
|
-
const existingBindings = existing["bindings_v2.json"];
|
|
391
|
-
const existingResources = Array.isArray(existingBindings?.resources)
|
|
392
|
-
? existingBindings.resources.filter(isJsonObject)
|
|
393
|
-
: [];
|
|
394
|
-
|
|
395
|
-
// buildConnectionResources throws when an Integration Service node's
|
|
396
|
-
// connection binding cannot be resolved (missing, literal, or malformed).
|
|
397
|
-
// Turn that into a specific reason here so `uip solution pack` reports
|
|
398
|
-
// the real cause instead of buildAsync's generic InternalError.
|
|
399
|
-
let resources: ReturnType<typeof buildConnectionResources>;
|
|
400
|
-
try {
|
|
401
|
-
resources = buildConnectionResources(processes, existingResources);
|
|
402
|
-
} catch (error) {
|
|
403
|
-
const message =
|
|
404
|
-
error instanceof Error ? error.message : String(error);
|
|
405
|
-
return `${bpmnFile} has an Integration Service connection binding that cannot be resolved — ${message}`;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const metadata = buildBpmnPackageMetadata({
|
|
409
|
-
bpmnFile,
|
|
410
|
-
entryPoints,
|
|
411
|
-
// Only entry-points.json and bindings_v2.json are consumed below,
|
|
412
|
-
// so this id is inert: operate.json stays owned by
|
|
413
|
-
// `ensureContentOperateFile`, which backfills the stable project id.
|
|
414
|
-
projectId: options.projectStorageId ?? crypto.randomUUID(),
|
|
415
|
-
resources,
|
|
416
|
-
existing,
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
// Deliberately NOT package-descriptor.json: the shared serializer emits
|
|
420
|
-
// flat paths for the project directory, while a package needs the
|
|
421
|
-
// `content/`-prefixed form that `createPackageDescriptor` writes.
|
|
422
|
-
for (const name of [
|
|
423
|
-
"entry-points.json",
|
|
424
|
-
"bindings_v2.json",
|
|
425
|
-
] as const satisfies readonly BpmnPackageFile[]) {
|
|
426
|
-
await this.fileSystem.writeFile(
|
|
427
|
-
Path.join(contentFolder, name),
|
|
428
|
-
formatBpmnPackageFile(metadata[name]),
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return undefined;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Decide which BPMN model the artifacts are derived from.
|
|
437
|
-
*
|
|
438
|
-
* `project.uiproj` declares the answer in `MainFile`, so it decides. A
|
|
439
|
-
* directory listing must not: `readdir` order is filesystem-dependent, and
|
|
440
|
-
* a project may legitimately hold several models — a called subprocess, or a
|
|
441
|
-
* second process kept alongside the main one. Deriving from the wrong one
|
|
442
|
-
* mints entry points for a process nobody invokes, which publishes cleanly
|
|
443
|
-
* and then faults at run time, so an ambiguous project is declined instead
|
|
444
|
-
* of guessed.
|
|
445
|
-
*
|
|
446
|
-
* `MainFile` is absent from hand-authored and older projects, so a lone
|
|
447
|
-
* `.bpmn` file still resolves without it — that is unambiguous by itself.
|
|
448
|
-
*
|
|
449
|
-
* Returns the file name, or the reason no single model could be identified.
|
|
450
|
-
*/
|
|
451
|
-
private async selectBpmnFile(
|
|
452
|
-
contentFolder: string,
|
|
453
|
-
): Promise<
|
|
454
|
-
| { file: string; reason?: undefined }
|
|
455
|
-
| { file?: undefined; reason: string }
|
|
456
|
-
> {
|
|
457
|
-
const entries = await this.fileSystem.readdir(contentFolder);
|
|
458
|
-
// Sorted so both the choice below and the reason reported to the user
|
|
459
|
-
// are stable across platforms.
|
|
460
|
-
const bpmnFiles = entries
|
|
461
|
-
.filter((entry) => entry.endsWith(".bpmn"))
|
|
462
|
-
.sort((left, right) => left.localeCompare(right));
|
|
463
|
-
if (bpmnFiles.length === 0) {
|
|
464
|
-
return { reason: "the project contains no .bpmn file." };
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const mainFile = await this.readMainFile(contentFolder);
|
|
468
|
-
if (mainFile !== undefined) {
|
|
469
|
-
if (bpmnFiles.includes(mainFile)) {
|
|
470
|
-
return { file: mainFile };
|
|
471
|
-
}
|
|
472
|
-
return {
|
|
473
|
-
reason:
|
|
474
|
-
`project.uiproj declares "MainFile": "${mainFile}", which is not one of ` +
|
|
475
|
-
`the project's BPMN models (${bpmnFiles.join(", ")}).`,
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
const [onlyFile] = bpmnFiles;
|
|
480
|
-
if (bpmnFiles.length === 1 && onlyFile !== undefined) {
|
|
481
|
-
return { file: onlyFile };
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
return {
|
|
485
|
-
reason:
|
|
486
|
-
`the project contains ${bpmnFiles.length} BPMN models ` +
|
|
487
|
-
`(${bpmnFiles.join(", ")}) and project.uiproj does not declare which one is ` +
|
|
488
|
-
`the main model in "MainFile".`,
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* `MainFile` from the staged `project.uiproj`, or `undefined` when the
|
|
494
|
-
* project does not declare one.
|
|
495
|
-
*/
|
|
496
|
-
private async readMainFile(
|
|
497
|
-
contentFolder: string,
|
|
498
|
-
): Promise<string | undefined> {
|
|
499
|
-
let project: UiProject | undefined;
|
|
500
|
-
try {
|
|
501
|
-
project = await this.getUiProjectAsync(contentFolder);
|
|
502
|
-
} catch (error) {
|
|
503
|
-
// A malformed project.uiproj is `validateAsync`'s to report, and it
|
|
504
|
-
// has already run unless the caller passed --skip-validate. Fall
|
|
505
|
-
// back to the single-model rule rather than failing the build here
|
|
506
|
-
// on a second, less specific diagnosis of the same file.
|
|
507
|
-
this.logger.warn(
|
|
508
|
-
`Could not read project.uiproj to identify the main BPMN model: ${
|
|
509
|
-
error instanceof Error ? error.message : String(error)
|
|
510
|
-
}`,
|
|
511
|
-
);
|
|
512
|
-
return undefined;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
// `UiProject` types MainFile as a required string, but the file is
|
|
516
|
-
// user-authored and older ones omit it — so the value is checked, not
|
|
517
|
-
// trusted.
|
|
518
|
-
const mainFile: unknown = project?.MainFile;
|
|
519
|
-
return typeof mainFile === "string" && mainFile.length > 0
|
|
520
|
-
? mainFile
|
|
521
|
-
: undefined;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Read the artifacts already staged in content/, so regeneration can carry
|
|
526
|
-
* forward author-owned detail. Absent or unparseable files are treated as
|
|
527
|
-
* absent — the model is the source of truth.
|
|
528
|
-
*/
|
|
529
|
-
private async readExistingArtifacts(
|
|
530
|
-
contentFolder: string,
|
|
531
|
-
parseOptionalJson: (
|
|
532
|
-
raw: string | null,
|
|
533
|
-
label: string,
|
|
534
|
-
) => JsonObject | null,
|
|
535
|
-
): Promise<Partial<Record<BpmnPackageFile, JsonObject | null>>> {
|
|
536
|
-
const existing: Partial<Record<BpmnPackageFile, JsonObject | null>> =
|
|
537
|
-
{};
|
|
538
|
-
for (const name of [
|
|
539
|
-
"entry-points.json",
|
|
540
|
-
"bindings_v2.json",
|
|
541
|
-
] as const satisfies readonly BpmnPackageFile[]) {
|
|
542
|
-
const raw = await this.fileSystem.readFile(
|
|
543
|
-
Path.join(contentFolder, name),
|
|
544
|
-
);
|
|
545
|
-
if (!raw) {
|
|
546
|
-
existing[name] = null;
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
const text =
|
|
550
|
-
typeof raw === "string" ? raw : new TextDecoder().decode(raw);
|
|
551
|
-
try {
|
|
552
|
-
existing[name] = parseOptionalJson(text, name);
|
|
553
|
-
} catch {
|
|
554
|
-
this.logger.warn(
|
|
555
|
-
`Ignoring unparseable ${name} in the project; regenerating it from the BPMN model.`,
|
|
556
|
-
);
|
|
557
|
-
existing[name] = null;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
return existing;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
/**
|
|
564
|
-
* Create operate.json file if it doesn't already exist
|
|
565
|
-
*/
|
|
566
|
-
private async createOperateFile(
|
|
567
|
-
options: IProjectBuildOptions,
|
|
568
|
-
contentFolder: string,
|
|
569
|
-
): Promise<void> {
|
|
570
|
-
await ensureContentOperateFile(this.fileSystem, {
|
|
571
|
-
contentFolder,
|
|
572
|
-
projectPath: options.projectPath,
|
|
573
|
-
projectStorageId: options.projectStorageId,
|
|
574
|
-
contentType: ProjectTypes.ProcessOrchestration,
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* Create package-descriptor.json file.
|
|
580
|
-
* Lists standard packaging files and .bpmn files from content/.
|
|
581
|
-
*/
|
|
582
|
-
private async createPackageDescriptor(
|
|
583
|
-
localBuildFolder: string,
|
|
584
|
-
contentFolder: string,
|
|
585
|
-
): Promise<void> {
|
|
586
|
-
const descriptorFiles: Record<string, string> = {};
|
|
587
|
-
|
|
588
|
-
// Standard packaging files
|
|
589
|
-
descriptorFiles[NugetConstants.OperateFileName] = Path.join(
|
|
590
|
-
NugetConstants.ContentFolderName,
|
|
591
|
-
NugetConstants.OperateFileName,
|
|
592
|
-
);
|
|
593
|
-
descriptorFiles[BpmnConstants.EntryPointsFileName] = Path.join(
|
|
594
|
-
NugetConstants.ContentFolderName,
|
|
595
|
-
BpmnConstants.EntryPointsFileName,
|
|
596
|
-
);
|
|
597
|
-
descriptorFiles[NugetConstants.BindingsFileId] = Path.join(
|
|
598
|
-
NugetConstants.ContentFolderName,
|
|
599
|
-
BpmnConstants.BindingsV2FileName,
|
|
600
|
-
);
|
|
601
|
-
|
|
602
|
-
// Add .bpmn files from content/
|
|
603
|
-
const contentFiles = await this.fileSystem.readdir(contentFolder);
|
|
604
|
-
for (const file of contentFiles) {
|
|
605
|
-
if (file.endsWith(".bpmn")) {
|
|
606
|
-
descriptorFiles[file] = Path.join(
|
|
607
|
-
NugetConstants.ContentFolderName,
|
|
608
|
-
file,
|
|
609
|
-
);
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
const packageDescriptorPath = Path.join(
|
|
614
|
-
localBuildFolder,
|
|
615
|
-
NugetConstants.ContentFolderName,
|
|
616
|
-
NugetConstants.PackageDescriptorFileName,
|
|
617
|
-
);
|
|
618
|
-
const packageDescriptorJson = JSON.stringify(
|
|
619
|
-
{
|
|
620
|
-
$schema:
|
|
621
|
-
"https://cloud.uipath.com/draft/2024-12/package-descriptor",
|
|
622
|
-
files: descriptorFiles,
|
|
623
|
-
},
|
|
624
|
-
null,
|
|
625
|
-
2,
|
|
626
|
-
);
|
|
627
|
-
await this.fileSystem.writeFile(
|
|
628
|
-
packageDescriptorPath,
|
|
629
|
-
packageDescriptorJson,
|
|
630
|
-
);
|
|
631
|
-
}
|
|
632
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// BPMN (ProcessOrchestration) tool factory.
|
|
2
|
-
//
|
|
3
|
-
// Self-registers on import so micro-frontend consumers (whose MFEs UiPath does
|
|
4
|
-
// not control) that load this package via a side-effect import get the factory
|
|
5
|
-
// registered automatically — StudioWeb does not reference this package, so it
|
|
6
|
-
// relies on self-registration. Also exported for consumers that register
|
|
7
|
-
// explicitly; re-registering the same factory class is an idempotent no-op.
|
|
8
|
-
import { toolsFactoryRepository } from "@uipath/solutionpackager-tool-core";
|
|
9
|
-
import { BpmnToolFactory } from "./bpmn-tool-factory.js";
|
|
10
|
-
|
|
11
|
-
toolsFactoryRepository.registerProjectToolFactory(new BpmnToolFactory());
|
|
12
|
-
|
|
13
|
-
export { BpmnToolFactory };
|