@uipath/solutionpackager-tool-core 0.0.33 → 1.196.0

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.js CHANGED
@@ -108,8 +108,9 @@ class ZipService {
108
108
  }
109
109
  async extractAsync(zipData, targetPath) {
110
110
  const normalizedTarget = targetPath.replace(/\/$/, "");
111
- await this.fileSystem.mkdir(normalizedTarget);
112
111
  const files = unzipSync(zipData);
112
+ const dirsToCreate = new Set([normalizedTarget]);
113
+ const filesToWrite = [];
113
114
  const extractedPaths = [];
114
115
  for (const [relativePath, data] of Object.entries(files)) {
115
116
  if (relativePath.endsWith("/") || data.length === 0) {
@@ -117,7 +118,7 @@ class ZipService {
117
118
  if (!dirPath.startsWith(`${normalizedTarget}/`)) {
118
119
  throw new Error(`Zip-slip detected: entry "${relativePath}" escapes extraction directory`);
119
120
  }
120
- await this.fileSystem.mkdir(dirPath);
121
+ dirsToCreate.add(dirPath);
121
122
  continue;
122
123
  }
123
124
  const fullPath = `${normalizedTarget}/${relativePath}`;
@@ -126,21 +127,26 @@ class ZipService {
126
127
  }
127
128
  const parentDir = fullPath.split("/").slice(0, -1).join("/");
128
129
  if (parentDir) {
129
- await this.fileSystem.mkdir(parentDir);
130
+ dirsToCreate.add(parentDir);
130
131
  }
131
- await this.fileSystem.writeFile(fullPath, data);
132
+ filesToWrite.push({ path: fullPath, data });
132
133
  extractedPaths.push(fullPath);
133
134
  }
135
+ await Promise.all(Array.from(dirsToCreate, (dir) => this.fileSystem.mkdir(dir)));
136
+ await Promise.all(filesToWrite.map(({ path, data }) => this.fileSystem.writeFile(path, data)));
134
137
  return extractedPaths;
135
138
  }
136
139
  async compressAsync(sourcePath) {
137
140
  const normalizedSource = sourcePath.replace(/\/$/, "");
138
141
  const entries = await Path.walkDirectory(this.fileSystem, normalizedSource);
142
+ const fileContents = await Promise.all(entries.map(async (entry) => ({
143
+ relativePath: entry.relativePath,
144
+ data: await this.fileSystem.readFile(entry.absolutePath)
145
+ })));
139
146
  const zippable = {};
140
- for (const entry of entries) {
141
- const fileData = await this.fileSystem.readFile(entry.absolutePath);
142
- if (fileData) {
143
- zippable[entry.relativePath] = fileData;
147
+ for (const { relativePath, data } of fileContents) {
148
+ if (data) {
149
+ zippable[relativePath] = data;
144
150
  }
145
151
  }
146
152
  const zipData = zipSync(zippable, {
@@ -1011,9 +1017,9 @@ class LogMessage {
1011
1017
  line;
1012
1018
  id;
1013
1019
  timestamp;
1014
- constructor(message, logLevel, options) {
1020
+ constructor(message, logLevel, options, timestamp) {
1015
1021
  this.message = message;
1016
- this.logLevel = logLevel;
1022
+ this.logLevel = LogMessage.toLogLevel(logLevel);
1017
1023
  this.source = options?.source;
1018
1024
  this.sourceTarget = options?.sourceTarget;
1019
1025
  this.progress = options?.progress;
@@ -1021,7 +1027,37 @@ class LogMessage {
1021
1027
  this.filePath = options?.filePath;
1022
1028
  this.line = options?.line;
1023
1029
  this.id = options?.id;
1024
- this.timestamp = new Date;
1030
+ this.timestamp = LogMessage.resolveTimestamp(timestamp);
1031
+ }
1032
+ static resolveTimestamp(timestamp) {
1033
+ if (timestamp instanceof Date) {
1034
+ return timestamp;
1035
+ }
1036
+ if (timestamp === undefined || timestamp === null) {
1037
+ return new Date;
1038
+ }
1039
+ const parsed = new Date(timestamp);
1040
+ if (Number.isNaN(parsed.getTime())) {
1041
+ console.error(`[LogMessage] invalid timestamp "${timestamp}", defaulting to now`);
1042
+ return new Date;
1043
+ }
1044
+ return parsed;
1045
+ }
1046
+ static toLogLevel(level) {
1047
+ switch (level) {
1048
+ case "Verbose":
1049
+ case "Trace":
1050
+ case "Debug":
1051
+ return "Debug" /* Debug */;
1052
+ case "Warn":
1053
+ case "Warning":
1054
+ return "Warning" /* Warn */;
1055
+ case "Error":
1056
+ case "Critical":
1057
+ return "Error" /* Error */;
1058
+ default:
1059
+ return "Information" /* Info */;
1060
+ }
1025
1061
  }
1026
1062
  toFormattedString() {
1027
1063
  const parts = [];
@@ -1100,6 +1136,7 @@ class ToolResult {
1100
1136
  errorCode;
1101
1137
  message;
1102
1138
  packages;
1139
+ details;
1103
1140
  constructor(errorCode, message, packages = []) {
1104
1141
  this.errorCode = errorCode;
1105
1142
  this.message = message;
@@ -1243,6 +1280,7 @@ class NugetPackager {
1243
1280
  class ProjectTool {
1244
1281
  fileSystem;
1245
1282
  logger;
1283
+ static ProjectFileName = "project.uiproj";
1246
1284
  constructor(fileSystem, logger) {
1247
1285
  this.fileSystem = fileSystem;
1248
1286
  this.logger = logger;
@@ -1263,6 +1301,18 @@ class ProjectTool {
1263
1301
  this.logger.info("Pack operation is a noop");
1264
1302
  return ToolResult.success();
1265
1303
  }
1304
+ async getUiProjectAsync(projectPath) {
1305
+ const filePath = Path.join(projectPath, ProjectTool.ProjectFileName);
1306
+ if (!await this.fileSystem.exists(filePath)) {
1307
+ return;
1308
+ }
1309
+ const raw = await this.fileSystem.readFile(filePath);
1310
+ if (!raw) {
1311
+ return;
1312
+ }
1313
+ const json = typeof raw === "string" ? raw : new TextDecoder("utf-8").decode(raw);
1314
+ return JSON.parse(json);
1315
+ }
1266
1316
  async dispose() {}
1267
1317
  }
1268
1318
  // src/services/solution-tool.ts
@@ -1297,8 +1347,11 @@ class ToolsFactoryRepository {
1297
1347
  solutionFactory = null;
1298
1348
  registerProjectToolFactory(factory) {
1299
1349
  for (const type of factory.supportedTypes) {
1300
- if (this.projectFactoryMap.has(type)) {
1301
- console.warn(`Tool factory already registered for project type '${type}', skipping.`);
1350
+ const existing = this.projectFactoryMap.get(type);
1351
+ if (existing) {
1352
+ if (existing.constructor?.name !== factory.constructor?.name) {
1353
+ console.warn(`Tool factory conflict for project type '${type}': ` + `'${existing.constructor?.name}' already registered, ` + `ignoring '${factory.constructor?.name}'.`);
1354
+ }
1302
1355
  continue;
1303
1356
  }
1304
1357
  this.projectFactoryMap.set(type, factory);
@@ -14,5 +14,6 @@ export { TargetFramework } from "./target-frameworks.js";
14
14
  export type { ToolErrorCode } from "./tool-error-code.js";
15
15
  export { ToolErrorCodes } from "./tool-error-code.js";
16
16
  export { ToolResult } from "./tool-result.js";
17
+ export type { UiProject } from "./ui-project.js";
17
18
  export type { UiPathProject } from "./uipath-project.js";
18
19
  export type { UiPathSolution } from "./uipath-solution.js";
@@ -15,7 +15,7 @@ export declare class LogMessage {
15
15
  line?: string;
16
16
  id?: string;
17
17
  readonly timestamp: Date;
18
- constructor(message: string, logLevel: LogLevel, options?: {
18
+ constructor(message: string, logLevel: LogLevel | string, options?: {
19
19
  source?: string;
20
20
  sourceTarget?: string;
21
21
  progress?: number;
@@ -23,6 +23,8 @@ export declare class LogMessage {
23
23
  filePath?: string;
24
24
  line?: string;
25
25
  id?: string;
26
- });
26
+ }, timestamp?: Date | string);
27
+ private static resolveTimestamp;
28
+ private static toLogLevel;
27
29
  toFormattedString(): string;
28
30
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -15,6 +15,10 @@ export declare class ToolResult {
15
15
  * List of packages produced
16
16
  */
17
17
  packages: string[];
18
+ /**
19
+ * Additional structured result data produced by a tool.
20
+ */
21
+ details?: Record<string, unknown>;
18
22
  constructor(errorCode: ToolErrorCode, message?: string, packages?: string[]);
19
23
  /**
20
24
  * Indicates whether the result represents a successful operation
@@ -0,0 +1,11 @@
1
+ /**
2
+ * DTO matching the project.uiproj file format.
3
+ */
4
+ export interface UiProject {
5
+ ProjectType: string;
6
+ /** Lowercase variant used by some project types (e.g., Flow) */
7
+ type?: string;
8
+ Name: string;
9
+ Description: string | null;
10
+ MainFile: string;
11
+ }
@@ -3,6 +3,14 @@
3
3
  * Maps file identifiers to their relative paths
4
4
  */
5
5
  export interface PackageDescriptorFileModel {
6
+ /**
7
+ * Schema URL identifying the descriptor version. Orchestrator's
8
+ * package extractor reads this to dispatch between the legacy
9
+ * project.json pipeline and the operate.json/entry-points/bindings
10
+ * pipeline; omitting it routes the package through the legacy
11
+ * pipeline and metadata extraction may silently fail.
12
+ */
13
+ $schema?: string;
6
14
  /**
7
15
  * Map of file identifiers to relative paths
8
16
  * Key: file identifier (e.g., 'operate.json', 'entrypoints.json', 'bindings')
@@ -0,0 +1,31 @@
1
+ import type { IFileSystem } from "@uipath/filesystem";
2
+ export interface ContentOperateFileParams {
3
+ /** The build's content/ folder (also where entry-points.json lives). */
4
+ contentFolder: string;
5
+ /** Already-resolved stable projectId to embed. */
6
+ projectId: string;
7
+ /** operate.json `contentType` for this tool (e.g. ProjectTypes.Api). */
8
+ contentType: string;
9
+ }
10
+ /**
11
+ * Ensure content/operate.json carries `projectId`. An existing file is
12
+ * backfilled in place; a missing one is scaffolded with the standard operate
13
+ * model for `contentType`, deriving `main` from entry-points.json. Shared by
14
+ * the packager tools that emit this same operate shape.
15
+ */
16
+ export declare function writeContentOperateFile(fs: IFileSystem, params: ContentOperateFileParams): Promise<void>;
17
+ export interface BuildContentOperateFileParams {
18
+ /** The build's content/ folder (also where entry-points.json lives). */
19
+ contentFolder: string;
20
+ /** Source project path used to read/persist a stable projectId. */
21
+ projectPath: string;
22
+ /** Orchestrator-supplied id; wins over a source-derived one when set. */
23
+ projectStorageId?: string;
24
+ /** operate.json `contentType` for this tool (e.g. ProjectTypes.Api). */
25
+ contentType: string;
26
+ }
27
+ /**
28
+ * Resolve a stable projectId (projectStorageId, else ensureProjectId on the
29
+ * source) and write content/operate.json via {@link writeContentOperateFile}.
30
+ */
31
+ export declare function ensureContentOperateFile(fs: IFileSystem, params: BuildContentOperateFileParams): Promise<void>;
@@ -0,0 +1,18 @@
1
+ import type { IFileSystem } from "@uipath/filesystem";
2
+ /**
3
+ * Resolve a stable projectId for a project source directory, patching it
4
+ * into operate.json (or legacy project.json) if missing. Returns a fresh
5
+ * unpersisted UUID when neither file exists.
6
+ */
7
+ export declare function ensureProjectId(projectPath: string, fs: IFileSystem): Promise<string>;
8
+ /**
9
+ * Backfill `projectId` in an existing operate.json (preserving other
10
+ * fields). No-op if the file is missing, malformed, or already has a
11
+ * non-empty `projectId`.
12
+ */
13
+ export declare function ensureProjectIdInOperateFile(operateFilePath: string, fallbackProjectId: string, fs: IFileSystem): Promise<void>;
14
+ /**
15
+ * If `operateFilePath` already exists, backfill its `projectId` with
16
+ * `projectId`; otherwise invoke `createIfMissing` to scaffold it.
17
+ */
18
+ export declare function writeOrBackfillOperateProjectId(operateFilePath: string, projectId: string, fs: IFileSystem, createIfMissing: () => Promise<void>): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import type { IFileSystem } from "../filesystem/file-system.js";
2
2
  import type { IProjectBuildOptions, IProjectPackOptions, IProjectRestoreOptions, IProjectValidateOptions } from "../models/project-options.js";
3
3
  import { ToolResult } from "../models/tool-result.js";
4
+ import type { UiProject } from "../models/ui-project.js";
4
5
  import type { IToolLogger } from "./tool-logger.js";
5
6
  /**
6
7
  * Base class for project tools.
@@ -8,6 +9,7 @@ import type { IToolLogger } from "./tool-logger.js";
8
9
  export declare abstract class ProjectTool {
9
10
  protected readonly fileSystem: IFileSystem;
10
11
  protected readonly logger: IToolLogger;
12
+ static readonly ProjectFileName = "project.uiproj";
11
13
  constructor(fileSystem: IFileSystem, logger: IToolLogger);
12
14
  /**
13
15
  * Restore project dependencies
@@ -25,6 +27,11 @@ export declare abstract class ProjectTool {
25
27
  * Pack project
26
28
  */
27
29
  packAsync(_options: IProjectPackOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
30
+ /**
31
+ * Read and parse `project.uiproj` from the project folder.
32
+ * Returns `undefined` for project types that do not have a `project.uiproj`
33
+ */
34
+ protected getUiProjectAsync(projectPath: string): Promise<UiProject | undefined>;
28
35
  /**
29
36
  * Dispose resources - override in derived classes to clean up
30
37
  */
@@ -49,7 +49,9 @@ export declare class ToolsFactoryRepository implements IToolsFactoryRepository,
49
49
  /**
50
50
  * Register a project tool factory.
51
51
  * Maps each of the factory's supported types to the factory.
52
- * Warns and skips if a project type is already registered.
52
+ * Re-registering the same factory for a type is a silent no-op (expected
53
+ * when tools that bundle their own copy share a command path). A different
54
+ * factory claiming an already-registered type is a conflict and warns.
53
55
  */
54
56
  registerProjectToolFactory(factory: IProjectToolFactory): void;
55
57
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/solutionpackager-tool-core",
3
- "version": "0.0.33",
3
+ "version": "1.196.0",
4
4
  "description": "UiPath contracts for solution packager tools",
5
5
  "type": "module",
6
6
  "exports": {
@@ -10,19 +10,6 @@
10
10
  }
11
11
  },
12
12
  "types": "./dist/index.d.ts",
13
- "scripts": {
14
- "build": "bun build ./src/index.ts --outdir dist --format esm --target browser --external @uipath/filesystem --external fflate && tsc --emitDeclarationOnly --outDir dist",
15
- "prepack": "bun run build",
16
- "publish:dry": "bun publish --dry-run",
17
- "publish:gh": "bun publish",
18
- "version:patch": "bun version patch --no-git-tag-version",
19
- "version:minor": "bun version minor --no-git-tag-version",
20
- "version:major": "bun version major --no-git-tag-version",
21
- "test": "vitest run",
22
- "test:coverage": "vitest run --coverage",
23
- "test:browser": "vitest run --config=vitest.browser.config.ts",
24
- "lint": "biome check ."
25
- },
26
13
  "files": [
27
14
  "dist",
28
15
  "!dist/**/*.map"
@@ -38,16 +25,16 @@
38
25
  "registry": "https://registry.npmjs.org/"
39
26
  },
40
27
  "devDependencies": {
41
- "@types/node": "^25.5.0",
42
- "@vitest/browser": "^4.0.14",
43
- "@vitest/browser-playwright": "^4.0.14",
44
- "@vitest/coverage-v8": "^4.0.14",
45
- "typescript": "^5.9.3",
46
- "vitest": "^4.0.14"
28
+ "@types/node": "^25.5.2",
29
+ "@vitest/browser": "^4.1.6",
30
+ "@vitest/browser-playwright": "^4.1.6",
31
+ "@vitest/coverage-v8": "^4.1.6",
32
+ "typescript": "^6.0.2",
33
+ "vitest": "^4.1.6"
47
34
  },
48
35
  "dependencies": {
49
- "@uipath/filesystem": "0.9.0",
36
+ "@uipath/filesystem": "1.196.0",
50
37
  "fflate": "^0.8.2"
51
38
  },
52
- "gitHead": "3f1b4d8e9f910be81e4cab956537f21dbd5d63ac"
39
+ "gitHead": "bed2b46f1ec33a47a7dcae2e6d4b7ab99bd06981"
53
40
  }