@uipath/packager-tool-businessrules 1.202.1 → 1.203.0-preview.180

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.
@@ -1,12 +1,13 @@
1
1
  import type { IProjectBuildOptions, IProjectPackOptions, IProjectRestoreOptions, IProjectValidateOptions } from "@uipath/solutionpackager-tool-core";
2
2
  import { ProjectTool, ToolResult } from "@uipath/solutionpackager-tool-core";
3
3
  /**
4
- * Business Rules project tool.
4
+ * Business Rules project tool. Only `packAsync` does anything.
5
5
  *
6
- * Only `packAsync` does anything. A rule ships as the `.dmn` itself, not wrapped
7
- * in a nupkg: `options.outputPath` is already `files/{projectId}` in the archive,
8
- * so copying the file there is the whole job. The solution's `businessRule`
9
- * resource points at it by file name.
6
+ * A rule ships as a `.uirule`, not wrapped in a nupkg: `options.outputPath` is already
7
+ * `files/{projectId}`, so writing the archive there is the whole job. The archive holds every
8
+ * `.dmn` plus `entry-points.json` at its root, and the manifest is where a deployed rule's argument
9
+ * contract is read from. Its `filePath` values keep a `/content/` prefix, which is an identifier
10
+ * inherited from the nupkg convention rather than a path into this archive.
10
11
  */
11
12
  export declare class BusinessRulesTool extends ProjectTool {
12
13
  restoreAsync(_options: IProjectRestoreOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
@@ -14,25 +15,47 @@ export declare class BusinessRulesTool extends ProjectTool {
14
15
  buildAsync(_options: IProjectBuildOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
15
16
  packAsync(options: IProjectPackOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
16
17
  /**
17
- * `{ruleName}-{version}.dmn`, which is what Orchestrator's export handler
18
+ * The v1 artefact: the rule's single document, copied under the resource's name.
19
+ *
20
+ * Reached only when the project has no manifest. One document, because a v1 rule is addressed by
21
+ * file name alone -- several would leave the extras unreferenced, since the project has a single
22
+ * `businessRule` resource.
23
+ */
24
+ private copyRuleDocumentAsync;
25
+ /**
26
+ * `{ruleName}-{version}.uirule`, which is what Orchestrator's export handler
18
27
  * builds for the same rule. Publish and export have to agree on this or a
19
28
  * rule stops round-tripping between solutions.
20
29
  *
21
- * `ruleName` is the name of the `businessRule` resource, never the `.dmn`
30
+ * `ruleName` is the name of the `businessRule` resource, never any `.dmn`
22
31
  * file's own basename — a project folder named "Business Rules" holding
23
- * "Business rule.dmn" is what Studio Web produces today. Orchestrator takes
24
- * the rule's name from the resource (`Spec.Name`) and rebuilds the file name
25
- * from it on export, and the service reads a rule's version back out of this
26
- * name by stripping the resource name off the front. Deriving the name from
27
- * the `.dmn` would break both.
32
+ * "Business rule.dmn" is what Studio Web produces today, and with several
33
+ * documents in the project no single basename could stand for the rule
34
+ * anyway. Orchestrator takes the rule's name from the resource (`Spec.Name`)
35
+ * and rebuilds the file name from it on export, and the service reads a
36
+ * rule's version back out of this name by stripping the resource name off
37
+ * the front. Deriving the name from a document would break both.
28
38
  *
29
39
  * So resolve it the way `solution projects add` does when it registers the
30
40
  * resource: `project.uiproj`'s `Name`, falling back to the folder. Blank also
31
41
  * falls back — `projects add` passes an empty `Name` through, but here it
32
- * would produce a file called "-1.0.0.dmn".
42
+ * would produce a file called "-1.0.0.uirule".
33
43
  *
34
44
  * Returns `undefined` when the declared name cannot be used in a file name.
35
45
  */
36
46
  private resolveRuleName;
47
+ /**
48
+ * The document names the manifest declares, or `null` when it cannot be read.
49
+ *
50
+ * `filePath` is an identifier of the form `/content/{file}#{decisionId}` -- the `/content/`
51
+ * prefix is inherited from the nupkg convention and does not describe the archive, which is
52
+ * flat -- so only the file name is kept.
53
+ *
54
+ * A manifest that declares no entry points is readable and returns an empty list. That is the
55
+ * caller's to allow: it ships a `.uirule` exposing nothing, which is what the designer wrote.
56
+ */
57
+ private readDeclaredDocuments;
58
+ /** Declared documents that are not among the packed entries. */
59
+ private declaredDocumentsMissingFrom;
37
60
  private findRuleFiles;
38
61
  }
package/dist/index.js CHANGED
@@ -11,9 +11,12 @@ import {
11
11
  Path,
12
12
  ProjectTool,
13
13
  ToolErrorCodes,
14
- ToolResult
14
+ ToolResult,
15
+ ZipService
15
16
  } from "@uipath/solutionpackager-tool-core";
16
17
  var DmnExtension = ".dmn";
18
+ var RuleArchiveExtension = ".uirule";
19
+ var EntryPointsFileName = "entry-points.json";
17
20
  var ReservedInFileName = /[<>:"/\\|?*]/;
18
21
  function isUsableInFileName(name) {
19
22
  return !ReservedInFileName.test(name) && ![...name].some((character) => character.charCodeAt(0) < 32);
@@ -38,22 +41,38 @@ class BusinessRulesTool extends ProjectTool {
38
41
  if (ruleFiles.length === 0) {
39
42
  return ToolResult.error(ToolErrorCodes.InternalError, `No ${DmnExtension} file found in ${options.projectPath}.`, "Add a decision to the project, then pack again.");
40
43
  }
41
- if (ruleFiles.length > 1) {
42
- return ToolResult.error(ToolErrorCodes.InternalError, `Found ${ruleFiles.length} ${DmnExtension} files in ${options.projectPath}, expected one.`, `Keep one decision per project and move the rest to their own projects.`);
43
- }
44
- const sourceFile = ruleFiles[0];
45
44
  const ruleName = await this.resolveRuleName(options);
46
45
  if (ruleName === undefined) {
47
46
  return ToolResult.error(ToolErrorCodes.InternalError, `The project name in project.uiproj cannot be used in a file name.`, `Rename the project so its name has no path separators and none of < > : " | ? * characters.`);
48
47
  }
49
- const targetName = `${ruleName}-${options.package.version}${DmnExtension}`;
50
- const targetFile = Path.join(options.outputPath, targetName);
51
- this.logger.progress(`Copying ${targetName}...`);
52
- const content = await this.fileSystem.readFile(sourceFile);
53
- if (!content) {
54
- return ToolResult.error(ToolErrorCodes.InternalError, `Could not read ${sourceFile}.`);
48
+ const manifest = await this.fileSystem.readFile(Path.join(options.projectPath, EntryPointsFileName));
49
+ if (!manifest || manifest.length === 0) {
50
+ this.logger.warn(`No ${EntryPointsFileName} in ${options.projectPath}, so the rule ships as a ${DmnExtension} and exposes no arguments. Open the project in Studio Web and save it to write its entry points.`);
51
+ return this.copyRuleDocumentAsync(options, ruleFiles, ruleName);
52
+ }
53
+ const declared = this.readDeclaredDocuments(manifest);
54
+ if (declared === null) {
55
+ return ToolResult.error(ToolErrorCodes.InternalError, `${EntryPointsFileName} in ${options.projectPath} is not valid JSON.`, `Open the project in Studio Web and save it to rewrite its entry points, then pack again.`);
56
+ }
57
+ const entries = {
58
+ [EntryPointsFileName]: manifest
59
+ };
60
+ for (const ruleFile of ruleFiles) {
61
+ const content = await this.fileSystem.readFile(ruleFile);
62
+ if (!content) {
63
+ return ToolResult.error(ToolErrorCodes.InternalError, `Could not read ${ruleFile}.`);
64
+ }
65
+ entries[Path.basename(ruleFile)] = content;
55
66
  }
56
- await this.fileSystem.writeFile(targetFile, content);
67
+ const missing = this.declaredDocumentsMissingFrom(declared, entries);
68
+ if (missing.length > 0) {
69
+ return ToolResult.error(ToolErrorCodes.InternalError, `${EntryPointsFileName} names ${missing.join(", ")}, which ${missing.length === 1 ? "is" : "are"} not in ${options.projectPath}.`, `Move every .dmn the rule uses to the top level of the project, then pack again.`);
70
+ }
71
+ const targetName = `${ruleName}-${options.package.version}${RuleArchiveExtension}`;
72
+ const targetFile = Path.join(options.outputPath, targetName);
73
+ this.logger.progress(`Packing ${ruleFiles.length} ${DmnExtension} file(s) into ${targetName}...`);
74
+ const archive = new ZipService(this.fileSystem).compressEntries(entries);
75
+ await this.fileSystem.writeFile(targetFile, archive);
57
76
  return new ToolResult(ToolErrorCodes.Success, "done", []);
58
77
  } catch (error) {
59
78
  const message = error instanceof Error ? error.toString() : String(error);
@@ -61,6 +80,19 @@ class BusinessRulesTool extends ProjectTool {
61
80
  return ToolResult.error(ToolErrorCodes.InternalError, "An error occurred while packing the Business Rules project");
62
81
  }
63
82
  }
83
+ async copyRuleDocumentAsync(options, ruleFiles, ruleName) {
84
+ if (ruleFiles.length > 1) {
85
+ return ToolResult.error(ToolErrorCodes.InternalError, `Found ${ruleFiles.length} ${DmnExtension} files in ${options.projectPath}, expected one.`, `Keep one decision per project and move the rest to their own projects, or save the project in Studio Web so its entry points name the callable one.`);
86
+ }
87
+ const content = await this.fileSystem.readFile(ruleFiles[0]);
88
+ if (!content) {
89
+ return ToolResult.error(ToolErrorCodes.InternalError, `Could not read ${ruleFiles[0]}.`);
90
+ }
91
+ const targetName = `${ruleName}-${options.package.version}${DmnExtension}`;
92
+ this.logger.progress(`Copying ${targetName}...`);
93
+ await this.fileSystem.writeFile(Path.join(options.outputPath, targetName), content);
94
+ return new ToolResult(ToolErrorCodes.Success, "done", []);
95
+ }
64
96
  async resolveRuleName(options) {
65
97
  const uiProject = await this.getUiProjectAsync(options.projectPath);
66
98
  const declared = uiProject?.Name?.trim();
@@ -69,6 +101,18 @@ class BusinessRulesTool extends ProjectTool {
69
101
  }
70
102
  return isUsableInFileName(declared) ? declared : undefined;
71
103
  }
104
+ readDeclaredDocuments(manifest) {
105
+ try {
106
+ const parsed = JSON.parse(new TextDecoder().decode(manifest));
107
+ return (parsed.entryPoints ?? []).map((entryPoint) => entryPoint.filePath).filter((filePath) => !!filePath).map((filePath) => Path.basename(filePath.split("#")[0]));
108
+ } catch {
109
+ return null;
110
+ }
111
+ }
112
+ declaredDocumentsMissingFrom(declared, entries) {
113
+ const packed = new Set(Object.keys(entries));
114
+ return [...new Set(declared)].filter((document) => !packed.has(document));
115
+ }
72
116
  async findRuleFiles(projectPath) {
73
117
  const entries = await this.fileSystem.readdir(projectPath);
74
118
  const ruleFiles = [];
@@ -103,4 +147,4 @@ export {
103
147
  BusinessRulesToolFactory
104
148
  };
105
149
 
106
- //# debugId=4D84B4901B7D0E3264756E2164756E21
150
+ //# debugId=FC8F16D6B30A334C64756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/packager-tool-businessrules",
3
- "version": "1.202.1",
3
+ "version": "1.203.0-preview.180",
4
4
  "description": "UiPath Business Rules tool implementation",
5
5
  "type": "module",
6
6
  "exports": {
@@ -18,10 +18,10 @@
18
18
  "files": [
19
19
  "dist"
20
20
  ],
21
- "author": "",
21
+ "author": "UiPath",
22
22
  "license": "SEE LICENSE IN LICENSE.txt",
23
23
  "peerDependencies": {
24
- "@uipath/solutionpackager-tool-core": "1.202.1"
24
+ "@uipath/solutionpackager-tool-core": "1.203.0"
25
25
  },
26
- "gitHead": "280b581b7893224d512dd194f47dd57243cc6a5c"
26
+ "gitHead": "4e0a51e3ab2c9d89afd3d35a7b087a7fa58133a0"
27
27
  }