@uipath/project-executor 1.200.0-preview.118
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/README.md +131 -0
- package/dist/index.js +252 -0
- package/dist/src/index.d.ts +16 -0
- package/dist/src/models/project-type.d.ts +19 -0
- package/dist/src/models/run-options.d.ts +149 -0
- package/dist/src/models/run-result.d.ts +67 -0
- package/dist/src/models/run-status.d.ts +34 -0
- package/dist/src/services/executor-factory.d.ts +21 -0
- package/dist/src/services/executor-logger.d.ts +14 -0
- package/dist/src/services/executor-tool-module.d.ts +39 -0
- package/dist/src/services/executors-factory-repository.d.ts +39 -0
- package/dist/src/services/project-executor.d.ts +77 -0
- package/dist/src/services/project-runner.d.ts +42 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# @uipath/project-executor
|
|
2
|
+
|
|
3
|
+
The common run/debug contract that every UiPath project type implements, so a
|
|
4
|
+
caller can run a project without knowing what kind of project it is.
|
|
5
|
+
|
|
6
|
+
Today each project type ships its own run/debug command with its own arguments,
|
|
7
|
+
its own result shape and its own status vocabulary:
|
|
8
|
+
|
|
9
|
+
| project type | command | model | status words |
|
|
10
|
+
|---|---|---|---|
|
|
11
|
+
| ProcessOrchestration (BPMN) | `uip maestro bpmn debug` | Studio Web upload → Orchestrator debug session → PIMS instance | `Completed`, `Faulted`, `Cancelled` |
|
|
12
|
+
| Flow | `uip maestro flow debug` | same | same |
|
|
13
|
+
| CaseManagement | `uip maestro case debug` | same | same |
|
|
14
|
+
| Agent | `uip agent debug` | serverless job, polled via Orchestrator Jobs | `Successful`, `Faulted`, `Stopped`, `Suspended` |
|
|
15
|
+
| API Workflow | `uip api-workflow run` | local, in-process | `Successful`, `Faulted` |
|
|
16
|
+
| RPA | external Studio backend | local or remote robot | its own set |
|
|
17
|
+
|
|
18
|
+
This package holds the types and interfaces those implementations share, so a
|
|
19
|
+
caller (for example a solution-level run command) can treat them uniformly.
|
|
20
|
+
It contains **no execution logic of its own** — only the contract and the
|
|
21
|
+
lifecycle around a run. Implementations are expected to be thin adapters over
|
|
22
|
+
the run/debug service each tool already owns, so a unified entry point and the
|
|
23
|
+
per-tool command cannot drift.
|
|
24
|
+
|
|
25
|
+
## What is in here
|
|
26
|
+
|
|
27
|
+
**The contract**
|
|
28
|
+
|
|
29
|
+
- `ProjectExecutor` — abstract base each project type extends. Operations
|
|
30
|
+
default to "not supported", so a type opts into exactly what it can do.
|
|
31
|
+
`runAsync` is the only operation today; interactive debugging (breakpoints,
|
|
32
|
+
stepping) is deliberately out of scope. Its `protected
|
|
33
|
+
resolveDefaultEntryPoint` is the single hook for "what runs when the caller
|
|
34
|
+
named nothing": the base reads the first entry point declared in
|
|
35
|
+
`entry-points.json`, and an implementation overrides it to put its own
|
|
36
|
+
convention first (RPA prefers `project.json`'s `main`). Implementations call it
|
|
37
|
+
from `runAsync`, so a caller that passed an entry point pays for no file read.
|
|
38
|
+
- `IProjectExecutorFactory` — declares `supportedTypes` and creates an executor.
|
|
39
|
+
- `ExecutorsFactoryRepository` (+ the shared `executorsFactoryRepository`)
|
|
40
|
+
— maps project types to factories. Same shape and registration semantics as
|
|
41
|
+
`ToolsFactoryRepository` in `@uipath/solutionpackager-tool-core`: the first
|
|
42
|
+
factory to claim a type keeps it, re-registering the same factory *class* is a
|
|
43
|
+
silent no-op (two tools each bundling a copy is normal), and only a different
|
|
44
|
+
class claiming a taken type warns.
|
|
45
|
+
|
|
46
|
+
The shared instance hangs off a `Symbol.for` slot on `globalThis`, for the
|
|
47
|
+
same reason `toolsFactoryRepository` does: tool bundles **inline** this
|
|
48
|
+
package (builds externalize only `applicationinsights` plus sibling entry
|
|
49
|
+
points), so every tool — including one built in a different repo, such as
|
|
50
|
+
`@uipath/rpa-tool` — ships its own copy of the module. With plain module
|
|
51
|
+
state each copy would get an independent map and cross-tool registration
|
|
52
|
+
would silently do nothing.
|
|
53
|
+
- `ExecutorFactoryModule` / `isExecutorFactoryModule` / `EXECUTOR_FACTORY_EXPORT`
|
|
54
|
+
— the shape of a tool's entry point. See below.
|
|
55
|
+
- `ProjectRunner` — the project-level layer. It owns the lifecycle around one
|
|
56
|
+
run (resolve the factory for the type, create the executor, invoke it, dispose
|
|
57
|
+
it) and turns an unregistered type or a throwing implementation into a
|
|
58
|
+
`RunResult`. It passes options through untouched — resolving a default entry
|
|
59
|
+
point needs I/O against a per-type layout, so that is the executor's job, not
|
|
60
|
+
the runner's. Callers compose this instead of
|
|
61
|
+
touching the registry themselves, the same way `@uipath/solution-packager`
|
|
62
|
+
builds on `@uipath/project-packager`.
|
|
63
|
+
|
|
64
|
+
**Normalized results**
|
|
65
|
+
|
|
66
|
+
- `RunStatus` / `mapRunStatus` — folds the three status vocabularies above onto
|
|
67
|
+
one enum, so callers switch on one thing and an unrecognized backend status
|
|
68
|
+
degrades to `Unknown` rather than throwing.
|
|
69
|
+
- `RunResult` / `RunErrorCode` / `RunStep` — a superset of what any one type
|
|
70
|
+
produces. Identifiers differ per backend (`jobKey` for Orchestrator,
|
|
71
|
+
`instanceId`/`runId` for PIMS, none for the local executor), so all are
|
|
72
|
+
optional and only `status` is guaranteed.
|
|
73
|
+
|
|
74
|
+
**Options**
|
|
75
|
+
|
|
76
|
+
- `ProjectRunOptions` / `SolutionRunOptions`, `RunConnection`, `RunCallbacks`,
|
|
77
|
+
`SolutionProjectRef`, `RunTarget`.
|
|
78
|
+
- `RunTarget.Online` is the only implemented target. `Local` exists so the
|
|
79
|
+
choice is explicit at the call site rather than implied.
|
|
80
|
+
- `IExecutorLogger` — a minimal logging surface, structurally satisfied by
|
|
81
|
+
`logger` from `@uipath/common`, declared here so this package does not force a
|
|
82
|
+
logging implementation on consumers.
|
|
83
|
+
|
|
84
|
+
## Implementing it
|
|
85
|
+
|
|
86
|
+
A tool exposes its factory from `src/executor-tool.ts`, which the host discovers
|
|
87
|
+
by file path. **A tool supplies a factory; it does not register anything.** The
|
|
88
|
+
module's only export is `createExecutorFactory`, and the host decides when, and
|
|
89
|
+
into which registry, to register what it hands back:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
// src/executor-tool.ts
|
|
93
|
+
import type { ExecutorFactoryModule } from "@uipath/project-executor";
|
|
94
|
+
import { MyExecutorFactory } from "./executor/my-executor-factory.js";
|
|
95
|
+
|
|
96
|
+
export const createExecutorFactory: ExecutorFactoryModule["createExecutorFactory"] =
|
|
97
|
+
() => new MyExecutorFactory();
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The host side imports it, checks the shape, and registers:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
const module = await import(executorToolPath);
|
|
104
|
+
if (!isExecutorFactoryModule(module)) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`${executorToolPath} does not export ${EXECUTOR_FACTORY_EXPORT}()`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
executorsFactoryRepository.registerProjectExecutorFactory(
|
|
110
|
+
module.createExecutorFactory(),
|
|
111
|
+
);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
This is the same split Studio Web uses for the packager tools, and it exists
|
|
115
|
+
because the alternative — a module that registers during import, as the older
|
|
116
|
+
`src/packager-tool.ts` convention does — is invisible at the call site and
|
|
117
|
+
order-dependent: a stray `import` anywhere silently changes which project types
|
|
118
|
+
look runnable, and the module cannot be imported without mutating global state,
|
|
119
|
+
which makes it awkward to test. With a factory supplier, a test calls
|
|
120
|
+
`createExecutorFactory()` and registers into its own
|
|
121
|
+
`new ExecutorsFactoryRepository()` instead of the process-wide one.
|
|
122
|
+
|
|
123
|
+
Two more rules worth knowing before adding a factory:
|
|
124
|
+
|
|
125
|
+
- **One factory per service, not per CLI verb.** A factory's `supportedTypes`
|
|
126
|
+
must list only the types its wrapped service actually handles. The `maestro`
|
|
127
|
+
verb, for instance, owns three project types but has three distinct debug
|
|
128
|
+
services that validate different project files — claiming all three from one
|
|
129
|
+
factory would route a project into the wrong validator.
|
|
130
|
+
- **`runAsync` should resolve, not reject, for expected failures.** A faulted
|
|
131
|
+
run is a `RunResult`, not an exception; reject only for programming errors.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// src/models/run-options.ts
|
|
2
|
+
var RunTarget;
|
|
3
|
+
((RunTarget2) => {
|
|
4
|
+
RunTarget2["Online"] = "Online";
|
|
5
|
+
RunTarget2["Local"] = "Local";
|
|
6
|
+
})(RunTarget ||= {});
|
|
7
|
+
// src/models/run-status.ts
|
|
8
|
+
var RunStatus;
|
|
9
|
+
((RunStatus2) => {
|
|
10
|
+
RunStatus2["Pending"] = "Pending";
|
|
11
|
+
RunStatus2["Running"] = "Running";
|
|
12
|
+
RunStatus2["Succeeded"] = "Succeeded";
|
|
13
|
+
RunStatus2["Faulted"] = "Faulted";
|
|
14
|
+
RunStatus2["Cancelled"] = "Cancelled";
|
|
15
|
+
RunStatus2["Suspended"] = "Suspended";
|
|
16
|
+
RunStatus2["Unknown"] = "Unknown";
|
|
17
|
+
})(RunStatus ||= {});
|
|
18
|
+
var TERMINAL_RUN_STATUSES = [
|
|
19
|
+
"Succeeded" /* Succeeded */,
|
|
20
|
+
"Faulted" /* Faulted */,
|
|
21
|
+
"Cancelled" /* Cancelled */
|
|
22
|
+
];
|
|
23
|
+
function isTerminalRunStatus(status) {
|
|
24
|
+
return TERMINAL_RUN_STATUSES.includes(status);
|
|
25
|
+
}
|
|
26
|
+
var RAW_STATUS_MAP = {
|
|
27
|
+
completed: "Succeeded" /* Succeeded */,
|
|
28
|
+
faulted: "Faulted" /* Faulted */,
|
|
29
|
+
cancelled: "Cancelled" /* Cancelled */,
|
|
30
|
+
canceled: "Cancelled" /* Cancelled */,
|
|
31
|
+
failed: "Faulted" /* Faulted */,
|
|
32
|
+
running: "Running" /* Running */,
|
|
33
|
+
inprogress: "Running" /* Running */,
|
|
34
|
+
pending: "Pending" /* Pending */,
|
|
35
|
+
successful: "Succeeded" /* Succeeded */,
|
|
36
|
+
stopped: "Cancelled" /* Cancelled */,
|
|
37
|
+
stopping: "Running" /* Running */,
|
|
38
|
+
suspended: "Suspended" /* Suspended */,
|
|
39
|
+
terminating: "Running" /* Running */,
|
|
40
|
+
new: "Pending" /* Pending */,
|
|
41
|
+
waitingforresource: "Pending" /* Pending */
|
|
42
|
+
};
|
|
43
|
+
function mapRunStatus(rawStatus) {
|
|
44
|
+
if (!rawStatus)
|
|
45
|
+
return "Unknown" /* Unknown */;
|
|
46
|
+
return RAW_STATUS_MAP[rawStatus.toLowerCase()] ?? "Unknown" /* Unknown */;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/models/run-result.ts
|
|
50
|
+
var RunErrorCode;
|
|
51
|
+
((RunErrorCode2) => {
|
|
52
|
+
RunErrorCode2["None"] = "None";
|
|
53
|
+
RunErrorCode2["InvalidInput"] = "InvalidInput";
|
|
54
|
+
RunErrorCode2["UnsupportedProjectType"] = "UnsupportedProjectType";
|
|
55
|
+
RunErrorCode2["Unauthorized"] = "Unauthorized";
|
|
56
|
+
RunErrorCode2["PrepareFailed"] = "PrepareFailed";
|
|
57
|
+
RunErrorCode2["StartFailed"] = "StartFailed";
|
|
58
|
+
RunErrorCode2["ExecutionFailed"] = "ExecutionFailed";
|
|
59
|
+
RunErrorCode2["Timeout"] = "Timeout";
|
|
60
|
+
RunErrorCode2["Cancelled"] = "Cancelled";
|
|
61
|
+
RunErrorCode2["Unknown"] = "Unknown";
|
|
62
|
+
})(RunErrorCode ||= {});
|
|
63
|
+
|
|
64
|
+
class RunResult {
|
|
65
|
+
status;
|
|
66
|
+
errorCode;
|
|
67
|
+
rawStatus;
|
|
68
|
+
message;
|
|
69
|
+
jobKey;
|
|
70
|
+
instanceId;
|
|
71
|
+
runId;
|
|
72
|
+
traceId;
|
|
73
|
+
steps;
|
|
74
|
+
output;
|
|
75
|
+
url;
|
|
76
|
+
details;
|
|
77
|
+
constructor(status, errorCode = "None" /* None */, message) {
|
|
78
|
+
this.status = status;
|
|
79
|
+
this.errorCode = errorCode;
|
|
80
|
+
this.message = message;
|
|
81
|
+
}
|
|
82
|
+
get isSuccess() {
|
|
83
|
+
return this.status === "Succeeded" /* Succeeded */ && this.errorCode === "None" /* None */;
|
|
84
|
+
}
|
|
85
|
+
get isTerminal() {
|
|
86
|
+
return isTerminalRunStatus(this.status);
|
|
87
|
+
}
|
|
88
|
+
static succeeded(init = {}) {
|
|
89
|
+
return Object.assign(new RunResult("Succeeded" /* Succeeded */, "None" /* None */), init);
|
|
90
|
+
}
|
|
91
|
+
static failed(errorCode, message, init = {}) {
|
|
92
|
+
return Object.assign(new RunResult("Faulted" /* Faulted */, errorCode, message), init);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// src/services/executor-logger.ts
|
|
96
|
+
var noopExecutorLogger = {
|
|
97
|
+
info: () => {},
|
|
98
|
+
warn: () => {},
|
|
99
|
+
error: () => {}
|
|
100
|
+
};
|
|
101
|
+
// src/services/executor-tool-module.ts
|
|
102
|
+
var EXECUTOR_FACTORY_EXPORT = "createExecutorFactory";
|
|
103
|
+
function isExecutorFactoryModule(module) {
|
|
104
|
+
return typeof module === "object" && module !== null && typeof module[EXECUTOR_FACTORY_EXPORT] === "function";
|
|
105
|
+
}
|
|
106
|
+
// src/services/executors-factory-repository.ts
|
|
107
|
+
class ExecutorsFactoryRepository {
|
|
108
|
+
factoryMap = new Map;
|
|
109
|
+
onConflict;
|
|
110
|
+
constructor(onConflict) {
|
|
111
|
+
this.onConflict = onConflict ?? ((message) => console.warn(message));
|
|
112
|
+
}
|
|
113
|
+
registerProjectExecutorFactory(factory) {
|
|
114
|
+
for (const projectType of factory.supportedTypes) {
|
|
115
|
+
const existing = this.factoryMap.get(projectType);
|
|
116
|
+
if (existing) {
|
|
117
|
+
if (existing.constructor?.name !== factory.constructor?.name) {
|
|
118
|
+
this.onConflict(`Executor factory conflict for project type '${projectType}': ` + `'${existing.constructor?.name}' already registered, ` + `ignoring '${factory.constructor?.name}'.`);
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
this.factoryMap.set(projectType, factory);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
canHandleProject(projectType) {
|
|
126
|
+
return this.factoryMap.has(projectType);
|
|
127
|
+
}
|
|
128
|
+
getProjectExecutorFactory(projectType) {
|
|
129
|
+
const factory = this.factoryMap.get(projectType);
|
|
130
|
+
if (!factory) {
|
|
131
|
+
throw new Error(`No executor is registered for project type '${projectType}'. ` + `Registered types: ${this.registeredTypes.join(", ") || "none"}.`);
|
|
132
|
+
}
|
|
133
|
+
return factory;
|
|
134
|
+
}
|
|
135
|
+
get registeredTypes() {
|
|
136
|
+
return [...this.factoryMap.keys()];
|
|
137
|
+
}
|
|
138
|
+
reset() {
|
|
139
|
+
this.factoryMap.clear();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
var REGISTRY_KEY = Symbol.for("@uipath/project-executor/executorsFactoryRepository");
|
|
143
|
+
var _global = globalThis;
|
|
144
|
+
if (!_global[REGISTRY_KEY]) {
|
|
145
|
+
_global[REGISTRY_KEY] = new ExecutorsFactoryRepository;
|
|
146
|
+
}
|
|
147
|
+
var executorsFactoryRepository = _global[REGISTRY_KEY];
|
|
148
|
+
// src/services/project-executor.ts
|
|
149
|
+
class ProjectExecutor {
|
|
150
|
+
fileSystem;
|
|
151
|
+
logger;
|
|
152
|
+
constructor(fileSystem, logger) {
|
|
153
|
+
this.fileSystem = fileSystem;
|
|
154
|
+
this.logger = logger;
|
|
155
|
+
}
|
|
156
|
+
runAsync(_options, _cancellationToken) {
|
|
157
|
+
return Promise.resolve(RunResult.failed("UnsupportedProjectType" /* UnsupportedProjectType */, `${this.constructor.name} does not support run.`));
|
|
158
|
+
}
|
|
159
|
+
async resolveDefaultEntryPoint(projectPath) {
|
|
160
|
+
const entryPointsFile = this.fileSystem.path.join(projectPath, ENTRY_POINTS_FILE_NAME);
|
|
161
|
+
try {
|
|
162
|
+
const raw = await this.fileSystem.readFile(entryPointsFile, "utf-8");
|
|
163
|
+
if (!raw) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const declared = JSON.parse(raw).entryPoints;
|
|
167
|
+
return this.firstDeclaredEntryPoint(declared);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
this.logger.warn(`Could not read the default entry point from ${entryPointsFile}: ${error instanceof Error ? error.message : String(error)}`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
asEntryPoint(value) {
|
|
174
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
175
|
+
}
|
|
176
|
+
firstDeclaredEntryPoint(entryPoints) {
|
|
177
|
+
if (!Array.isArray(entryPoints)) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
for (const entry of entryPoints) {
|
|
181
|
+
const filePath = this.asEntryPoint(entry?.filePath);
|
|
182
|
+
if (filePath) {
|
|
183
|
+
return filePath;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
dispose() {
|
|
189
|
+
return Promise.resolve();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
var ENTRY_POINTS_FILE_NAME = "entry-points.json";
|
|
193
|
+
// src/services/project-runner.ts
|
|
194
|
+
class ProjectRunner {
|
|
195
|
+
fileSystem;
|
|
196
|
+
logger;
|
|
197
|
+
registry;
|
|
198
|
+
constructor(deps) {
|
|
199
|
+
this.fileSystem = deps.fileSystem;
|
|
200
|
+
this.logger = deps.logger ?? noopExecutorLogger;
|
|
201
|
+
this.registry = deps.registry ?? executorsFactoryRepository;
|
|
202
|
+
}
|
|
203
|
+
canRun(projectType) {
|
|
204
|
+
return this.registry.canHandleProject(projectType);
|
|
205
|
+
}
|
|
206
|
+
get runnableTypes() {
|
|
207
|
+
return this.registry.registeredTypes;
|
|
208
|
+
}
|
|
209
|
+
async runAsync(options, cancellationToken) {
|
|
210
|
+
const { projectType, name } = options.project;
|
|
211
|
+
if (!this.canRun(projectType)) {
|
|
212
|
+
const known = this.runnableTypes.join(", ") || "none";
|
|
213
|
+
return RunResult.failed("UnsupportedProjectType" /* UnsupportedProjectType */, `No executor is registered for project type '${projectType}' ` + `(project '${name}'). Runnable types: ${known}.`);
|
|
214
|
+
}
|
|
215
|
+
let executor;
|
|
216
|
+
try {
|
|
217
|
+
const factory = this.registry.getProjectExecutorFactory(projectType);
|
|
218
|
+
executor = await factory.createAsync(this.logger, this.fileSystem);
|
|
219
|
+
this.logger.info(`Running ${name} (${projectType}) on ${options.target}.`);
|
|
220
|
+
return await executor.runAsync(options, cancellationToken);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
return RunResult.failed("Unknown" /* Unknown */, messageOf(error));
|
|
223
|
+
} finally {
|
|
224
|
+
try {
|
|
225
|
+
await executor?.dispose();
|
|
226
|
+
} catch (error) {
|
|
227
|
+
this.logger.warn(`Failed to dispose the ${projectType} executor: ${messageOf(error)}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function messageOf(error) {
|
|
233
|
+
return error instanceof Error ? error.message : String(error);
|
|
234
|
+
}
|
|
235
|
+
export {
|
|
236
|
+
noopExecutorLogger,
|
|
237
|
+
mapRunStatus,
|
|
238
|
+
isTerminalRunStatus,
|
|
239
|
+
isExecutorFactoryModule,
|
|
240
|
+
executorsFactoryRepository,
|
|
241
|
+
TERMINAL_RUN_STATUSES,
|
|
242
|
+
RunTarget,
|
|
243
|
+
RunStatus,
|
|
244
|
+
RunResult,
|
|
245
|
+
RunErrorCode,
|
|
246
|
+
ProjectRunner,
|
|
247
|
+
ProjectExecutor,
|
|
248
|
+
ExecutorsFactoryRepository,
|
|
249
|
+
EXECUTOR_FACTORY_EXPORT
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
//# debugId=F5B1472AD9D0537464756E2164756E21
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type { ProjectType } from "./models/project-type";
|
|
2
|
+
export type { ProjectRunOptions, RunCallbacks, RunConnection, SolutionProjectRef, SolutionRunOptions, } from "./models/run-options";
|
|
3
|
+
export { RunTarget } from "./models/run-options";
|
|
4
|
+
export type { RunStep } from "./models/run-result";
|
|
5
|
+
export { RunErrorCode, RunResult } from "./models/run-result";
|
|
6
|
+
export { isTerminalRunStatus, mapRunStatus, RunStatus, TERMINAL_RUN_STATUSES, } from "./models/run-status";
|
|
7
|
+
export type { IProjectExecutorFactory } from "./services/executor-factory";
|
|
8
|
+
export type { IExecutorLogger } from "./services/executor-logger";
|
|
9
|
+
export { noopExecutorLogger } from "./services/executor-logger";
|
|
10
|
+
export type { ExecutorFactoryModule } from "./services/executor-tool-module";
|
|
11
|
+
export { EXECUTOR_FACTORY_EXPORT, isExecutorFactoryModule, } from "./services/executor-tool-module";
|
|
12
|
+
export type { IExecutorsFactoryRepository, IExecutorsFactoryRepositoryConfigurator, } from "./services/executors-factory-repository";
|
|
13
|
+
export { ExecutorsFactoryRepository, executorsFactoryRepository, } from "./services/executors-factory-repository";
|
|
14
|
+
export { ProjectExecutor } from "./services/project-executor";
|
|
15
|
+
export type { ProjectRunnerDeps } from "./services/project-runner";
|
|
16
|
+
export { ProjectRunner } from "./services/project-runner";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A project type as it appears in a solution manifest — `Process`, `Agent`,
|
|
3
|
+
* `processOrchestration`, and whatever a future tool introduces.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately a plain string rather than a union with an enum. Manifests are
|
|
6
|
+
* written by more than one producer with inconsistent casing (solution-sdk
|
|
7
|
+
* stores `ProcessOrchestration` lower-cased because that is what Studio Web
|
|
8
|
+
* expects), and a solution may legitimately name a type this package has never
|
|
9
|
+
* heard of. The alias this replaced — `ProjectTypes | string` from
|
|
10
|
+
* `@uipath/solutionpackager-tool-core` — collapsed to `string` regardless, so it
|
|
11
|
+
* validated nothing while making the *run* contract depend on the *packaging*
|
|
12
|
+
* contract and drag its dependencies along behind it.
|
|
13
|
+
*
|
|
14
|
+
* The real contract is elsewhere: an executor declares the exact strings it
|
|
15
|
+
* claims via `IProjectExecutorFactory.supportedTypes`, and the registry matches
|
|
16
|
+
* them verbatim. A caller reading a manifest is responsible for handing over the
|
|
17
|
+
* spelling those executors registered.
|
|
18
|
+
*/
|
|
19
|
+
export type ProjectType = string;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { ProjectType } from "./project-type";
|
|
2
|
+
import type { RunStep } from "./run-result";
|
|
3
|
+
import type { RunStatus } from "./run-status";
|
|
4
|
+
/**
|
|
5
|
+
* Where the run executes. Only `Online` is implemented today; the meeting
|
|
6
|
+
* decision was online (serverless) first, with local/hybrid later, so the
|
|
7
|
+
* enum exists to keep that choice explicit at the call site rather than
|
|
8
|
+
* implied.
|
|
9
|
+
*/
|
|
10
|
+
export declare enum RunTarget {
|
|
11
|
+
/** Serverless execution in the cloud. */
|
|
12
|
+
Online = "Online",
|
|
13
|
+
/** Local execution on the developer machine. Not implemented yet. */
|
|
14
|
+
Local = "Local"
|
|
15
|
+
}
|
|
16
|
+
/** Auth/tenant coordinates a project type needs to reach the backends. */
|
|
17
|
+
export interface RunConnection {
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
accessToken: string;
|
|
20
|
+
organizationId: string;
|
|
21
|
+
organizationName: string;
|
|
22
|
+
tenantId?: string;
|
|
23
|
+
tenantName?: string;
|
|
24
|
+
/** Orchestrator folder (OrganizationUnitId) the job runs in. */
|
|
25
|
+
organizationUnitId?: number;
|
|
26
|
+
folderKey?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Fully-qualified Orchestrator folder path, e.g. `Shared/Finance`.
|
|
29
|
+
*
|
|
30
|
+
* Some runtimes want the path rather than the id or key — the RPA backend
|
|
31
|
+
* takes it verbatim — and it cannot be derived from either without a lookup.
|
|
32
|
+
*/
|
|
33
|
+
folderPath?: string;
|
|
34
|
+
}
|
|
35
|
+
/** Progress callbacks. All optional — a caller may just await the result. */
|
|
36
|
+
export interface RunCallbacks {
|
|
37
|
+
/** Fired when the normalized status changes. */
|
|
38
|
+
onStatusChange?: (status: RunStatus, steps?: RunStep[]) => void;
|
|
39
|
+
/** Fired for log lines emitted by the running project. */
|
|
40
|
+
onLog?: (message: string) => void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A project inside the solution, as seen by the executor layer.
|
|
44
|
+
*
|
|
45
|
+
* `designId` is the id from the solution file (`.uipx` `Projects[].Id`);
|
|
46
|
+
* `cloudProjectId` is the Studio Web project id, which only exists once the
|
|
47
|
+
* solution has been synced to the cloud.
|
|
48
|
+
*/
|
|
49
|
+
export interface SolutionProjectRef {
|
|
50
|
+
/** Project name as it appears in the solution. */
|
|
51
|
+
name: string;
|
|
52
|
+
projectType: ProjectType;
|
|
53
|
+
/** Absolute path to the project directory. */
|
|
54
|
+
projectPath: string;
|
|
55
|
+
/** Design-time id from the solution file. */
|
|
56
|
+
designId?: string;
|
|
57
|
+
/** Studio Web project id, once synced. */
|
|
58
|
+
cloudProjectId?: string;
|
|
59
|
+
}
|
|
60
|
+
/** Options for running one project. */
|
|
61
|
+
export interface ProjectRunOptions {
|
|
62
|
+
/** The project to run. */
|
|
63
|
+
project: SolutionProjectRef;
|
|
64
|
+
/**
|
|
65
|
+
* Entry point within the project (e.g. `Main.xaml`, `agent.json`, a
|
|
66
|
+
* workflow id). When omitted the implementation's default is used.
|
|
67
|
+
*/
|
|
68
|
+
entryPoint?: string;
|
|
69
|
+
/** Input arguments passed to the run. */
|
|
70
|
+
inputArguments?: Record<string, unknown>;
|
|
71
|
+
target: RunTarget;
|
|
72
|
+
connection: RunConnection;
|
|
73
|
+
/** Studio Web solution id, once the solution has been synced. */
|
|
74
|
+
solutionId?: string;
|
|
75
|
+
/**
|
|
76
|
+
* The other projects in the same solution. Implementations that support JIT
|
|
77
|
+
* resolution use these to let the runtime resolve cross-project calls
|
|
78
|
+
* without publishing.
|
|
79
|
+
*
|
|
80
|
+
* Note for whoever builds the FPS `debug.master` map: Studio Web puts
|
|
81
|
+
* *every* JIT-enabled project into `processes`, including the lead one
|
|
82
|
+
* (the lead is identified separately by `debug.master.projectId`). So the
|
|
83
|
+
* map is built from `[project, ...siblingProjects]`, not from
|
|
84
|
+
* `siblingProjects` alone.
|
|
85
|
+
*/
|
|
86
|
+
siblingProjects?: SolutionProjectRef[];
|
|
87
|
+
/** Milliseconds between status polls. */
|
|
88
|
+
pollIntervalMs?: number;
|
|
89
|
+
/** Overall budget for waiting on the run, in milliseconds. */
|
|
90
|
+
timeoutMs?: number;
|
|
91
|
+
callbacks?: RunCallbacks;
|
|
92
|
+
/**
|
|
93
|
+
* Whether to wait for the run to finish. Defaults to `true` when omitted.
|
|
94
|
+
*
|
|
95
|
+
* With `false` the implementation should return as soon as the run has
|
|
96
|
+
* started, leaving `status` non-terminal (`Pending`/`Running`). It is a real
|
|
97
|
+
* field rather than a `providerOptions` key because "start it and don't
|
|
98
|
+
* block" is meaningful for every project type, not one of them.
|
|
99
|
+
*
|
|
100
|
+
* Honouring it is best-effort: some backends only expose a blocking call
|
|
101
|
+
* (a local RPA run through Studio/Helm, for one), so an implementation that
|
|
102
|
+
* cannot start-without-waiting still returns a normal terminal `RunResult`
|
|
103
|
+
* rather than failing. Callers must therefore read {@link RunResult.isTerminal}
|
|
104
|
+
* instead of inferring the outcome from having passed `wait: false`.
|
|
105
|
+
*/
|
|
106
|
+
wait?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Run knobs that belong to one project type rather than to every type.
|
|
109
|
+
* Keys are owned and interpreted by the implementing executor.
|
|
110
|
+
*
|
|
111
|
+
* The escape hatch exists so type-specific switches do not have to be
|
|
112
|
+
* promoted into this shared shape one at a time. RPA, for example, has
|
|
113
|
+
* `logLevel`, `skipBuild`, `profiling` and `profilingMode`; none of those
|
|
114
|
+
* mean anything to a serverless agent run. It mirrors how
|
|
115
|
+
* `RunResult.details` already carries type-specific data on the way out.
|
|
116
|
+
*
|
|
117
|
+
* A knob that turns out to be genuinely cross-cutting should graduate to a
|
|
118
|
+
* real field instead of living here forever.
|
|
119
|
+
*/
|
|
120
|
+
providerOptions?: Record<string, unknown>;
|
|
121
|
+
}
|
|
122
|
+
/** Options for running a whole solution. */
|
|
123
|
+
export interface SolutionRunOptions {
|
|
124
|
+
/** Path to the solution directory or `.uis`/`.uipx` file. */
|
|
125
|
+
solutionPath: string;
|
|
126
|
+
/**
|
|
127
|
+
* Name or design id of the lead project — the one whose entry point is
|
|
128
|
+
* executed. Required when the solution has more than one runnable project.
|
|
129
|
+
*/
|
|
130
|
+
leadProject?: string;
|
|
131
|
+
entryPoint?: string;
|
|
132
|
+
inputArguments?: Record<string, unknown>;
|
|
133
|
+
target: RunTarget;
|
|
134
|
+
connection: RunConnection;
|
|
135
|
+
pollIntervalMs?: number;
|
|
136
|
+
timeoutMs?: number;
|
|
137
|
+
callbacks?: RunCallbacks;
|
|
138
|
+
/**
|
|
139
|
+
* When false, return as soon as the run starts instead of awaiting it.
|
|
140
|
+
* Forwarded to the lead project's executor — see
|
|
141
|
+
* {@link ProjectRunOptions.wait} for what implementations guarantee.
|
|
142
|
+
*/
|
|
143
|
+
wait?: boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Passed through to the lead project's executor. See
|
|
146
|
+
* {@link ProjectRunOptions.providerOptions}.
|
|
147
|
+
*/
|
|
148
|
+
providerOptions?: Record<string, unknown>;
|
|
149
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { RunStatus } from "./run-status";
|
|
2
|
+
/** Why a run failed, for callers that need to branch without parsing messages. */
|
|
3
|
+
export declare enum RunErrorCode {
|
|
4
|
+
None = "None",
|
|
5
|
+
/** The project/solution path or entry point could not be resolved. */
|
|
6
|
+
InvalidInput = "InvalidInput",
|
|
7
|
+
/** No executor is registered for the project type. */
|
|
8
|
+
UnsupportedProjectType = "UnsupportedProjectType",
|
|
9
|
+
/** Authentication or authorization failed. */
|
|
10
|
+
Unauthorized = "Unauthorized",
|
|
11
|
+
/** The pre-run (JIT) preparation step failed. */
|
|
12
|
+
PrepareFailed = "PrepareFailed",
|
|
13
|
+
/** The job could not be started. */
|
|
14
|
+
StartFailed = "StartFailed",
|
|
15
|
+
/** The run started but ended in a non-success terminal state. */
|
|
16
|
+
ExecutionFailed = "ExecutionFailed",
|
|
17
|
+
/** Polling/waiting exceeded the allotted budget. */
|
|
18
|
+
Timeout = "Timeout",
|
|
19
|
+
/** Cancelled via the cancellation token. */
|
|
20
|
+
Cancelled = "Cancelled",
|
|
21
|
+
/** Anything not covered above. */
|
|
22
|
+
Unknown = "Unknown"
|
|
23
|
+
}
|
|
24
|
+
/** One execution step reported by a project type, when it exposes them. */
|
|
25
|
+
export interface RunStep {
|
|
26
|
+
id: string;
|
|
27
|
+
name?: string;
|
|
28
|
+
type?: string;
|
|
29
|
+
status: RunStatus;
|
|
30
|
+
startedAt?: string;
|
|
31
|
+
completedAt?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Result of running a single project.
|
|
35
|
+
*
|
|
36
|
+
* Deliberately a superset of what any one project type produces: identifiers
|
|
37
|
+
* differ per backend (`jobKey` for Orchestrator, `instanceId`/`runId` for
|
|
38
|
+
* PIMS), so all are optional and only `status` is guaranteed.
|
|
39
|
+
*/
|
|
40
|
+
export declare class RunResult {
|
|
41
|
+
status: RunStatus;
|
|
42
|
+
errorCode: RunErrorCode;
|
|
43
|
+
/** The project type's own status string, preserved for diagnostics. */
|
|
44
|
+
rawStatus?: string;
|
|
45
|
+
message?: string;
|
|
46
|
+
/** Orchestrator job key, when the run went through a job. */
|
|
47
|
+
jobKey?: string;
|
|
48
|
+
/** PIMS debug instance id, when the run went through debug-instances. */
|
|
49
|
+
instanceId?: string;
|
|
50
|
+
runId?: string;
|
|
51
|
+
/** Distributed-trace id, when the runtime supplied one. */
|
|
52
|
+
traceId?: string;
|
|
53
|
+
/** Per-step detail, when the project type reports it. */
|
|
54
|
+
steps?: RunStep[];
|
|
55
|
+
/** Output arguments, when the run produced any. */
|
|
56
|
+
output?: Record<string, unknown>;
|
|
57
|
+
/** Deep link to the run in Studio Web / Orchestrator, when known. */
|
|
58
|
+
url?: string;
|
|
59
|
+
/** Anything project-type-specific that does not fit above. */
|
|
60
|
+
details?: Record<string, unknown>;
|
|
61
|
+
constructor(status: RunStatus, errorCode?: RunErrorCode, message?: string);
|
|
62
|
+
/** True only for a successfully completed run. */
|
|
63
|
+
get isSuccess(): boolean;
|
|
64
|
+
get isTerminal(): boolean;
|
|
65
|
+
static succeeded(init?: Partial<RunResult>): RunResult;
|
|
66
|
+
static failed(errorCode: RunErrorCode, message?: string, init?: Partial<RunResult>): RunResult;
|
|
67
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized terminal/intermediate status for a run across all project types.
|
|
3
|
+
*
|
|
4
|
+
* Each project type reports its own vocabulary — PIMS uses
|
|
5
|
+
* `Completed/Faulted/Cancelled`, Orchestrator uses
|
|
6
|
+
* `Successful/Faulted/Stopped/Suspended`, the local serverless executor uses
|
|
7
|
+
* its own — so implementations map onto this set and callers switch on one
|
|
8
|
+
* thing. `mapRunStatus` covers the values seen across the current tools.
|
|
9
|
+
*/
|
|
10
|
+
export declare enum RunStatus {
|
|
11
|
+
/** The job/instance has not started executing yet. */
|
|
12
|
+
Pending = "Pending",
|
|
13
|
+
/** Executing. */
|
|
14
|
+
Running = "Running",
|
|
15
|
+
/** Finished successfully. */
|
|
16
|
+
Succeeded = "Succeeded",
|
|
17
|
+
/** Finished with an error. */
|
|
18
|
+
Faulted = "Faulted",
|
|
19
|
+
/** Stopped or cancelled before completing. */
|
|
20
|
+
Cancelled = "Cancelled",
|
|
21
|
+
/** Parked waiting on external input (e.g. an Action Center task). */
|
|
22
|
+
Suspended = "Suspended",
|
|
23
|
+
/** Reported a status this layer does not recognize — see `rawStatus`. */
|
|
24
|
+
Unknown = "Unknown"
|
|
25
|
+
}
|
|
26
|
+
/** Statuses from which no further transition is expected. */
|
|
27
|
+
export declare const TERMINAL_RUN_STATUSES: readonly RunStatus[];
|
|
28
|
+
export declare function isTerminalRunStatus(status: RunStatus): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Map a project-type-specific status string onto the normalized `RunStatus`.
|
|
31
|
+
* Unrecognized values become `Unknown` rather than throwing — a new backend
|
|
32
|
+
* status must not fail a run that otherwise succeeded.
|
|
33
|
+
*/
|
|
34
|
+
export declare function mapRunStatus(rawStatus: string | undefined): RunStatus;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { IFileSystem } from "@uipath/filesystem";
|
|
2
|
+
import type { ProjectType } from "../models/project-type";
|
|
3
|
+
import type { IExecutorLogger } from "./executor-logger";
|
|
4
|
+
import type { ProjectExecutor } from "./project-executor";
|
|
5
|
+
/**
|
|
6
|
+
* Factory for creating a `ProjectExecutor`.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors `IProjectToolFactory` from the packager family: a tool package
|
|
9
|
+
* exports one of these declaring which project types it handles, and the
|
|
10
|
+
* registry maps each type to it.
|
|
11
|
+
*/
|
|
12
|
+
export interface IProjectExecutorFactory {
|
|
13
|
+
/** The project types this factory can run. */
|
|
14
|
+
readonly supportedTypes: readonly ProjectType[];
|
|
15
|
+
/**
|
|
16
|
+
* Create a executor instance.
|
|
17
|
+
* @param logger - logger for the operation
|
|
18
|
+
* @param fileSystem - file system instance
|
|
19
|
+
*/
|
|
20
|
+
createAsync(logger: IExecutorLogger, fileSystem: IFileSystem): Promise<ProjectExecutor>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal logging surface the executor layer needs.
|
|
3
|
+
*
|
|
4
|
+
* Structurally satisfied by `logger` from `@uipath/common`, so callers can pass
|
|
5
|
+
* that directly; declared here so `project-executor` does not force a logging
|
|
6
|
+
* implementation on consumers and so tests can substitute a spy.
|
|
7
|
+
*/
|
|
8
|
+
export interface IExecutorLogger {
|
|
9
|
+
info(message: string, ...args: unknown[]): void;
|
|
10
|
+
warn(message: string, ...args: unknown[]): void;
|
|
11
|
+
error(message: string, ...args: unknown[]): void;
|
|
12
|
+
}
|
|
13
|
+
/** Discards everything. Useful as a default and in tests. */
|
|
14
|
+
export declare const noopExecutorLogger: IExecutorLogger;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IProjectExecutorFactory } from "./executor-factory";
|
|
2
|
+
/** The export a tool's `executor-tool.ts` entry point must provide. */
|
|
3
|
+
export declare const EXECUTOR_FACTORY_EXPORT = "createExecutorFactory";
|
|
4
|
+
/**
|
|
5
|
+
* The shape of a tool's `src/executor-tool.ts` entry point.
|
|
6
|
+
*
|
|
7
|
+
* A tool **supplies a factory; it does not register anything.** The host imports
|
|
8
|
+
* the module, calls this, and registers the result into the one shared registry
|
|
9
|
+
* itself — the same division StudioWeb's `_registerPackagerTools` uses for the
|
|
10
|
+
* packager factories ("the packager packages no longer self-register on
|
|
11
|
+
* import").
|
|
12
|
+
*
|
|
13
|
+
* Registering on import, or handing the tool the registry to register into,
|
|
14
|
+
* both put the decision in the wrong place: the host ends up with entries it
|
|
15
|
+
* never asked for, import order starts to matter, and a stray `import` silently
|
|
16
|
+
* changes which project types look runnable. Exporting a factory keeps the
|
|
17
|
+
* tool's job "describe what I can run" and the host's job "decide what is
|
|
18
|
+
* registered".
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* // src/executor-tool.ts
|
|
22
|
+
* import type { ExecutorFactoryModule } from "@uipath/project-executor";
|
|
23
|
+
* import { MyExecutorFactory } from "./executor/my-executor-factory.js";
|
|
24
|
+
*
|
|
25
|
+
* export const createExecutorFactory: ExecutorFactoryModule["createExecutorFactory"] =
|
|
26
|
+
* () => new MyExecutorFactory();
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export interface ExecutorFactoryModule {
|
|
30
|
+
createExecutorFactory: () => IProjectExecutorFactory;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Narrow a dynamically imported module to {@link ExecutorFactoryModule}.
|
|
34
|
+
*
|
|
35
|
+
* Entry points are resolved by file path and imported dynamically, so the host
|
|
36
|
+
* has no compile-time guarantee about what came back. Checking here lets it fail
|
|
37
|
+
* with a message naming the missing export rather than a bare "not a function".
|
|
38
|
+
*/
|
|
39
|
+
export declare function isExecutorFactoryModule(module: unknown): module is ExecutorFactoryModule;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ProjectType } from "../models/project-type";
|
|
2
|
+
import type { IProjectExecutorFactory } from "./executor-factory";
|
|
3
|
+
/** Registration side of the repository. */
|
|
4
|
+
export interface IExecutorsFactoryRepositoryConfigurator {
|
|
5
|
+
registerProjectExecutorFactory(factory: IProjectExecutorFactory): void;
|
|
6
|
+
}
|
|
7
|
+
/** Lookup side of the repository. */
|
|
8
|
+
export interface IExecutorsFactoryRepository {
|
|
9
|
+
/** True when some factory claims this project type. */
|
|
10
|
+
canHandleProject(projectType: ProjectType): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Get the factory for a project type.
|
|
13
|
+
* @throws when no factory is registered for the type.
|
|
14
|
+
*/
|
|
15
|
+
getProjectExecutorFactory(projectType: ProjectType): IProjectExecutorFactory;
|
|
16
|
+
/** Project types with a registered factory. */
|
|
17
|
+
readonly registeredTypes: readonly ProjectType[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Maps project types to the factory that can run them.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately the same shape as `ToolsFactoryRepository` in the packager
|
|
23
|
+
* family, including the re-registration semantics: a tool package that bundles
|
|
24
|
+
* its own copy of a factory may register the same type twice, which is a no-op,
|
|
25
|
+
* while a *different* factory claiming a taken type is a real conflict and
|
|
26
|
+
* warns rather than silently winning.
|
|
27
|
+
*/
|
|
28
|
+
export declare class ExecutorsFactoryRepository implements IExecutorsFactoryRepository, IExecutorsFactoryRepositoryConfigurator {
|
|
29
|
+
private readonly factoryMap;
|
|
30
|
+
private readonly onConflict;
|
|
31
|
+
constructor(onConflict?: (message: string) => void);
|
|
32
|
+
registerProjectExecutorFactory(factory: IProjectExecutorFactory): void;
|
|
33
|
+
canHandleProject(projectType: ProjectType): boolean;
|
|
34
|
+
getProjectExecutorFactory(projectType: ProjectType): IProjectExecutorFactory;
|
|
35
|
+
get registeredTypes(): readonly ProjectType[];
|
|
36
|
+
/** Clear all registrations. Tests only. */
|
|
37
|
+
reset(): void;
|
|
38
|
+
}
|
|
39
|
+
export declare const executorsFactoryRepository: ExecutorsFactoryRepository;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { IFileSystem } from "@uipath/filesystem";
|
|
2
|
+
import type { ProjectRunOptions } from "../models/run-options";
|
|
3
|
+
import { RunResult } from "../models/run-result";
|
|
4
|
+
import type { IExecutorLogger } from "./executor-logger";
|
|
5
|
+
/**
|
|
6
|
+
* The contract every project type implements so `uip solution run` behaves the
|
|
7
|
+
* same regardless of what is inside the solution.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors `ProjectTool` from the packager family: an abstract base whose
|
|
10
|
+
* operations default to "not supported", so a project type opts in to exactly
|
|
11
|
+
* the operations it can do. Today that is `runAsync` only — debug (breakpoints,
|
|
12
|
+
* stepping) is deliberately out of scope, see docs/solution-run-debug-design.md.
|
|
13
|
+
*
|
|
14
|
+
* Implementations are thin adapters over each tool's existing run/debug
|
|
15
|
+
* service; they should not re-implement execution.
|
|
16
|
+
*/
|
|
17
|
+
export declare abstract class ProjectExecutor {
|
|
18
|
+
protected readonly fileSystem: IFileSystem;
|
|
19
|
+
protected readonly logger: IExecutorLogger;
|
|
20
|
+
constructor(fileSystem: IFileSystem, logger: IExecutorLogger);
|
|
21
|
+
/**
|
|
22
|
+
* Run the project and, unless the caller opts out, wait for it to finish.
|
|
23
|
+
*
|
|
24
|
+
* Implementations must resolve with a `RunResult` for expected failures
|
|
25
|
+
* (a faulted run is a result, not an exception) and reject only for
|
|
26
|
+
* programming errors.
|
|
27
|
+
*/
|
|
28
|
+
runAsync(_options: ProjectRunOptions, _cancellationToken?: AbortSignal): Promise<RunResult>;
|
|
29
|
+
/**
|
|
30
|
+
* The entry point to run when the caller named none.
|
|
31
|
+
*
|
|
32
|
+
* The one hook for "what runs by default". The shared implementation returns
|
|
33
|
+
* the first entry point the project declares in `entry-points.json` — the
|
|
34
|
+
* same file, shape and "first one wins" rule the packager uses to report a
|
|
35
|
+
* packaged project's entry point, so a project runs the workflow it publishes
|
|
36
|
+
* as its own.
|
|
37
|
+
*
|
|
38
|
+
* `protected`, and called by an implementation's own `runAsync` rather than
|
|
39
|
+
* by `ProjectRunner`. Two reasons: resolving a real default needs I/O against
|
|
40
|
+
* a layout only the project type understands, and only `runAsync` knows
|
|
41
|
+
* whether it needs one at all — a caller that passed an entry point should
|
|
42
|
+
* not pay for a file read. It also keeps one method where there were nearly
|
|
43
|
+
* two: a synchronous `defaultEntryPoint()` could only ever answer for a fixed
|
|
44
|
+
* name, so every project type whose default lives in a file had to leave it
|
|
45
|
+
* returning `undefined` and do the real work elsewhere.
|
|
46
|
+
*
|
|
47
|
+
* Override to put a project type's own convention first and delegate here for
|
|
48
|
+
* the rest — the RPA executor prefers `project.json`'s `main` and falls back
|
|
49
|
+
* to this. Best-effort by contract: a missing or malformed file resolves to
|
|
50
|
+
* `undefined` rather than failing a run the caller may have given an explicit
|
|
51
|
+
* entry point for.
|
|
52
|
+
*
|
|
53
|
+
* @param projectPath Absolute path to the project directory.
|
|
54
|
+
*/
|
|
55
|
+
protected resolveDefaultEntryPoint(projectPath: string): Promise<string | undefined>;
|
|
56
|
+
/**
|
|
57
|
+
* A declared entry-point path, when it is a usable one.
|
|
58
|
+
*
|
|
59
|
+
* `private`: it belongs to the default discovery below, not to the contract
|
|
60
|
+
* and not to overrides. A project type reading a field of its own vets it
|
|
61
|
+
* against its own rules.
|
|
62
|
+
*/
|
|
63
|
+
private asEntryPoint;
|
|
64
|
+
/**
|
|
65
|
+
* The first usable `filePath` in `entry-points.json`'s list.
|
|
66
|
+
*
|
|
67
|
+
* Scans rather than indexing `[0]` so one malformed element does not hide the
|
|
68
|
+
* rest of a list that does name a workflow.
|
|
69
|
+
*
|
|
70
|
+
* `private` for the same reason as {@link asEntryPoint}: this is how the
|
|
71
|
+
* default discovery reads *this* file. A project type with a list of its own
|
|
72
|
+
* elsewhere reads that itself.
|
|
73
|
+
*/
|
|
74
|
+
private firstDeclaredEntryPoint;
|
|
75
|
+
/** Release resources. Override when the implementation holds any. */
|
|
76
|
+
dispose(): Promise<void>;
|
|
77
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { IFileSystem } from "@uipath/filesystem";
|
|
2
|
+
import type { ProjectType } from "../models/project-type";
|
|
3
|
+
import type { ProjectRunOptions } from "../models/run-options";
|
|
4
|
+
import { RunResult } from "../models/run-result";
|
|
5
|
+
import type { IExecutorLogger } from "./executor-logger";
|
|
6
|
+
import type { IExecutorsFactoryRepository } from "./executors-factory-repository";
|
|
7
|
+
export interface ProjectRunnerDeps {
|
|
8
|
+
fileSystem: IFileSystem;
|
|
9
|
+
logger?: IExecutorLogger;
|
|
10
|
+
/** Defaults to the shared registry. Injectable for tests. */
|
|
11
|
+
registry?: IExecutorsFactoryRepository;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Runs a single project by resolving the executor registered for its type.
|
|
15
|
+
*
|
|
16
|
+
* This is the project-level layer: it owns the lifecycle around a run — look up
|
|
17
|
+
* the executor, create it, invoke it, dispose it, and turn an implementation
|
|
18
|
+
* that throws into a failed `RunResult` — so no caller has to repeat that.
|
|
19
|
+
*
|
|
20
|
+
* Callers compose this rather than touching the registry themselves, the same
|
|
21
|
+
* way `@uipath/solution-packager` builds on `@uipath/project-packager` instead of
|
|
22
|
+
* reaching for tool factories directly.
|
|
23
|
+
*/
|
|
24
|
+
export declare class ProjectRunner {
|
|
25
|
+
private readonly fileSystem;
|
|
26
|
+
private readonly logger;
|
|
27
|
+
private readonly registry;
|
|
28
|
+
constructor(deps: ProjectRunnerDeps);
|
|
29
|
+
/** True when some executor claims this project type. */
|
|
30
|
+
canRun(projectType: ProjectType): boolean;
|
|
31
|
+
/** Project types that can be run in this process. */
|
|
32
|
+
get runnableTypes(): readonly ProjectType[];
|
|
33
|
+
/**
|
|
34
|
+
* Run one project to completion.
|
|
35
|
+
*
|
|
36
|
+
* Resolves as a failed `RunResult` rather than rejecting for anything the
|
|
37
|
+
* caller can act on — an unregistered project type, or an executor that
|
|
38
|
+
* threw instead of returning a result. A caller reporting a run outcome
|
|
39
|
+
* should not have to also handle exceptions for ordinary failures.
|
|
40
|
+
*/
|
|
41
|
+
runAsync(options: ProjectRunOptions, cancellationToken?: AbortSignal): Promise<RunResult>;
|
|
42
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uipath/project-executor",
|
|
3
|
+
"license": "MIT",
|
|
4
|
+
"version": "1.200.0-preview.118",
|
|
5
|
+
"description": "Common run contract implemented by every UiPath project type - the shared ProjectExecutor interface behind 'uip solution run'.",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/UiPath/cli.git",
|
|
9
|
+
"directory": "packages/executor/project-executor"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"registry": "https://registry.npmjs.org/"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"uipath",
|
|
16
|
+
"executor",
|
|
17
|
+
"run",
|
|
18
|
+
"solution"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/src/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/src/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@uipath/filesystem": "1.200.0"
|
|
34
|
+
},
|
|
35
|
+
"gitHead": "bc87399d98869787498783b3b9383df4426fd896"
|
|
36
|
+
}
|