@uipath/packager-tool-workflowcompiler 1.199.0 → 1.199.1-preview.119
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 +73 -14
- package/dist/output-entry.d.ts +8 -2
- package/package.json +2 -2
- package/src/output-entry.ts +8 -2
- package/src/workflow-compiler-executor.ts +102 -8
package/dist/index.js
CHANGED
|
@@ -1157,6 +1157,7 @@ class WorkflowCompilerExecutor {
|
|
|
1157
1157
|
return new Promise((resolve) => {
|
|
1158
1158
|
let result = null;
|
|
1159
1159
|
const fallbackErrors = [];
|
|
1160
|
+
const analyzerFindings = [];
|
|
1160
1161
|
const childProcess = this.spawnProcess(executable, commandArgs, workflowCompilerConfig.dotnetEnv);
|
|
1161
1162
|
let stdoutEnded = false;
|
|
1162
1163
|
let stderrEnded = false;
|
|
@@ -1172,7 +1173,7 @@ class WorkflowCompilerExecutor {
|
|
|
1172
1173
|
errorCode: result.errorCode
|
|
1173
1174
|
}));
|
|
1174
1175
|
const toolResult = new ToolResult(result.errorCode, result.message, result.outputPackages);
|
|
1175
|
-
toolResult.details = getAdditionalResultDetails(result);
|
|
1176
|
+
toolResult.details = withAnalyzerFindings(getAdditionalResultDetails(result), analyzerFindings);
|
|
1176
1177
|
resolve(toolResult);
|
|
1177
1178
|
} else {
|
|
1178
1179
|
const exitCode = childProcess.exitCode ?? -1;
|
|
@@ -1202,8 +1203,8 @@ class WorkflowCompilerExecutor {
|
|
|
1202
1203
|
checkComplete();
|
|
1203
1204
|
}
|
|
1204
1205
|
};
|
|
1205
|
-
this.setupStdoutHandling(childProcess, streamCompletion, (r) => result = r);
|
|
1206
|
-
this.setupStderrHandling(childProcess, streamCompletion, fallbackErrors);
|
|
1206
|
+
this.setupStdoutHandling(childProcess, streamCompletion, (r) => result = r, analyzerFindings);
|
|
1207
|
+
this.setupStderrHandling(childProcess, streamCompletion, fallbackErrors, analyzerFindings);
|
|
1207
1208
|
this.setupProcessEventHandlers(childProcess, streamCompletion, resolve);
|
|
1208
1209
|
this.setupCancellationHandler(childProcess, cancellationToken, resolve);
|
|
1209
1210
|
});
|
|
@@ -1225,7 +1226,7 @@ class WorkflowCompilerExecutor {
|
|
|
1225
1226
|
command: fullCommand.join(" ")
|
|
1226
1227
|
}));
|
|
1227
1228
|
}
|
|
1228
|
-
setupStdoutHandling(childProcess, streamCompletion, onResult) {
|
|
1229
|
+
setupStdoutHandling(childProcess, streamCompletion, onResult, analyzerFindings) {
|
|
1229
1230
|
if (!childProcess.stdout) {
|
|
1230
1231
|
streamCompletion.markStdoutEnded();
|
|
1231
1232
|
return;
|
|
@@ -1239,24 +1240,29 @@ class WorkflowCompilerExecutor {
|
|
|
1239
1240
|
for (const line of lines) {
|
|
1240
1241
|
if (!line.trim())
|
|
1241
1242
|
continue;
|
|
1242
|
-
this.processOutputLine(line, onResult);
|
|
1243
|
+
this.processOutputLine(line, onResult, analyzerFindings);
|
|
1243
1244
|
}
|
|
1244
1245
|
});
|
|
1245
1246
|
childProcess.stdout.on("end", () => {
|
|
1246
1247
|
if (buffer.trim()) {
|
|
1247
|
-
this.processOutputLine(buffer, onResult);
|
|
1248
|
+
this.processOutputLine(buffer, onResult, analyzerFindings);
|
|
1248
1249
|
}
|
|
1249
1250
|
streamCompletion.markStdoutEnded();
|
|
1250
1251
|
});
|
|
1251
1252
|
}
|
|
1252
|
-
processOutputLine(line, onResult) {
|
|
1253
|
+
processOutputLine(line, onResult, analyzerFindings) {
|
|
1253
1254
|
const entry = this.parseOutputEntry(line);
|
|
1254
1255
|
if (!entry)
|
|
1255
1256
|
return;
|
|
1256
1257
|
if (entry.type === "Result") {
|
|
1257
1258
|
onResult(entry);
|
|
1258
1259
|
} else if (entry.type === "Log") {
|
|
1259
|
-
|
|
1260
|
+
const log = entry;
|
|
1261
|
+
const finding = toAnalyzerFinding(log);
|
|
1262
|
+
if (finding) {
|
|
1263
|
+
analyzerFindings.push(finding);
|
|
1264
|
+
}
|
|
1265
|
+
this.logMessage(log);
|
|
1260
1266
|
} else if (entry.type === "Progress") {
|
|
1261
1267
|
this.logProgress(entry);
|
|
1262
1268
|
}
|
|
@@ -1279,7 +1285,7 @@ class WorkflowCompilerExecutor {
|
|
|
1279
1285
|
const separator = percent && progress.message ? " - " : "";
|
|
1280
1286
|
this.logger.progress(`${percent}${separator}${progress.message ?? ""}`);
|
|
1281
1287
|
}
|
|
1282
|
-
setupStderrHandling(childProcess, streamCompletion, fallbackErrors) {
|
|
1288
|
+
setupStderrHandling(childProcess, streamCompletion, fallbackErrors, analyzerFindings) {
|
|
1283
1289
|
if (!childProcess.stderr) {
|
|
1284
1290
|
streamCompletion.markStderrEnded();
|
|
1285
1291
|
return;
|
|
@@ -1293,20 +1299,25 @@ class WorkflowCompilerExecutor {
|
|
|
1293
1299
|
for (const line of lines) {
|
|
1294
1300
|
if (!line.trim())
|
|
1295
1301
|
continue;
|
|
1296
|
-
this.processErrorLine(line, fallbackErrors);
|
|
1302
|
+
this.processErrorLine(line, fallbackErrors, analyzerFindings);
|
|
1297
1303
|
}
|
|
1298
1304
|
});
|
|
1299
1305
|
childProcess.stderr.on("end", () => {
|
|
1300
1306
|
if (buffer.trim()) {
|
|
1301
|
-
this.processErrorLine(buffer, fallbackErrors);
|
|
1307
|
+
this.processErrorLine(buffer, fallbackErrors, analyzerFindings);
|
|
1302
1308
|
}
|
|
1303
1309
|
streamCompletion.markStderrEnded();
|
|
1304
1310
|
});
|
|
1305
1311
|
}
|
|
1306
|
-
processErrorLine(line, fallbackErrors) {
|
|
1312
|
+
processErrorLine(line, fallbackErrors, analyzerFindings) {
|
|
1307
1313
|
const entry = this.parseOutputEntry(line);
|
|
1308
1314
|
if (entry && entry.type === "Log") {
|
|
1309
|
-
|
|
1315
|
+
const log = entry;
|
|
1316
|
+
const finding = toAnalyzerFinding(log);
|
|
1317
|
+
if (finding) {
|
|
1318
|
+
analyzerFindings.push(finding);
|
|
1319
|
+
}
|
|
1320
|
+
this.logger.error(log.message);
|
|
1310
1321
|
} else {
|
|
1311
1322
|
fallbackErrors.push(line);
|
|
1312
1323
|
this.logger.error(line);
|
|
@@ -1370,6 +1381,54 @@ function getAdditionalResultDetails(result) {
|
|
|
1370
1381
|
}
|
|
1371
1382
|
return Object.keys(details).length > 0 ? details : undefined;
|
|
1372
1383
|
}
|
|
1384
|
+
var ANALYZER_LOG_SOURCE = "Analyzer";
|
|
1385
|
+
function withAnalyzerFindings(details, findings) {
|
|
1386
|
+
const fromEnvelope = details?.findings;
|
|
1387
|
+
if (findings.length === 0 || Array.isArray(fromEnvelope) && fromEnvelope.length > 0) {
|
|
1388
|
+
return details;
|
|
1389
|
+
}
|
|
1390
|
+
return { ...details, findings };
|
|
1391
|
+
}
|
|
1392
|
+
function toAnalyzerFinding(log) {
|
|
1393
|
+
if (log.source !== ANALYZER_LOG_SOURCE) {
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
const message = parseAnalyzerMessage(log.data) ?? {};
|
|
1397
|
+
const activityId = message.ActivityId;
|
|
1398
|
+
const item = message.Item;
|
|
1399
|
+
return {
|
|
1400
|
+
...message,
|
|
1401
|
+
ErrorCode: message.ErrorCode ?? log.code,
|
|
1402
|
+
Description: message.Description ?? log.message,
|
|
1403
|
+
FilePath: message.FilePath ?? log.filePath,
|
|
1404
|
+
ErrorSeverity: message.ErrorSeverity ?? toErrorSeverity(log.logLevel),
|
|
1405
|
+
ActivityIdRef: activityId?.IdRef ?? log.id,
|
|
1406
|
+
AnalyzerItemName: item?.Name,
|
|
1407
|
+
AnalyzerItemType: item?.Type
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
function toErrorSeverity(logLevel) {
|
|
1411
|
+
switch (logLevel) {
|
|
1412
|
+
case "Debug":
|
|
1413
|
+
case "Trace":
|
|
1414
|
+
return "Verbose";
|
|
1415
|
+
case "Information":
|
|
1416
|
+
return "Info";
|
|
1417
|
+
default:
|
|
1418
|
+
return logLevel;
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
function parseAnalyzerMessage(data) {
|
|
1422
|
+
if (!data) {
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
try {
|
|
1426
|
+
const parsed = JSON.parse(data);
|
|
1427
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : undefined;
|
|
1428
|
+
} catch {
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1373
1432
|
|
|
1374
1433
|
// src/workflow-compiler-tool.ts
|
|
1375
1434
|
class WorkflowCompilerTool extends ProjectTool {
|
|
@@ -1666,4 +1725,4 @@ export {
|
|
|
1666
1725
|
WorkflowCompilerToolFactory
|
|
1667
1726
|
};
|
|
1668
1727
|
|
|
1669
|
-
//# debugId=
|
|
1728
|
+
//# debugId=9E5F0B597C7A3E6E64756E2164756E21
|
package/dist/output-entry.d.ts
CHANGED
|
@@ -15,12 +15,18 @@ export interface IOutputMessage {
|
|
|
15
15
|
inputId?: string;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Log message from workflow compiler output
|
|
18
|
+
* Log message from workflow compiler output. Analyzer findings are reported only here.
|
|
19
19
|
*/
|
|
20
20
|
export interface IOutputLog extends IOutputMessage {
|
|
21
21
|
type: OutputMessageType.Log;
|
|
22
22
|
message: string;
|
|
23
|
-
logLevel: "Information" | "Warning" | "Error";
|
|
23
|
+
logLevel: "Trace" | "Debug" | "Information" | "Warning" | "Error" | "None";
|
|
24
|
+
source?: string;
|
|
25
|
+
code?: string;
|
|
26
|
+
filePath?: string;
|
|
27
|
+
id?: string;
|
|
28
|
+
/** Serialized `AnalyzerMessage`. */
|
|
29
|
+
data?: string;
|
|
24
30
|
}
|
|
25
31
|
/**
|
|
26
32
|
* Progress update from workflow compiler output
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/packager-tool-workflowcompiler",
|
|
3
|
-
"version": "1.199.
|
|
3
|
+
"version": "1.199.1-preview.119",
|
|
4
4
|
"description": "UiPath Workflow Compiler tool implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -28,5 +28,5 @@
|
|
|
28
28
|
"@uipath/solutionpackager-tool-core": "1.199.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "9237653cf731fb275714c8ee5aab5dc057424b42"
|
|
32
32
|
}
|
package/src/output-entry.ts
CHANGED
|
@@ -17,12 +17,18 @@ export interface IOutputMessage {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Log message from workflow compiler output
|
|
20
|
+
* Log message from workflow compiler output. Analyzer findings are reported only here.
|
|
21
21
|
*/
|
|
22
22
|
export interface IOutputLog extends IOutputMessage {
|
|
23
23
|
type: OutputMessageType.Log;
|
|
24
24
|
message: string;
|
|
25
|
-
logLevel: "Information" | "Warning" | "Error";
|
|
25
|
+
logLevel: "Trace" | "Debug" | "Information" | "Warning" | "Error" | "None";
|
|
26
|
+
source?: string;
|
|
27
|
+
code?: string;
|
|
28
|
+
filePath?: string;
|
|
29
|
+
id?: string;
|
|
30
|
+
/** Serialized `AnalyzerMessage`. */
|
|
31
|
+
data?: string;
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
/**
|
|
@@ -82,6 +82,7 @@ export class WorkflowCompilerExecutor {
|
|
|
82
82
|
return new Promise((resolve) => {
|
|
83
83
|
let result: IOutputResult | null = null;
|
|
84
84
|
const fallbackErrors: string[] = [];
|
|
85
|
+
const analyzerFindings: Record<string, unknown>[] = [];
|
|
85
86
|
|
|
86
87
|
const childProcess = this.spawnProcess(
|
|
87
88
|
executable,
|
|
@@ -115,7 +116,10 @@ export class WorkflowCompilerExecutor {
|
|
|
115
116
|
result.message,
|
|
116
117
|
result.outputPackages,
|
|
117
118
|
);
|
|
118
|
-
toolResult.details =
|
|
119
|
+
toolResult.details = withAnalyzerFindings(
|
|
120
|
+
getAdditionalResultDetails(result),
|
|
121
|
+
analyzerFindings,
|
|
122
|
+
);
|
|
119
123
|
resolve(toolResult);
|
|
120
124
|
} else {
|
|
121
125
|
const exitCode = childProcess.exitCode ?? -1;
|
|
@@ -163,11 +167,13 @@ export class WorkflowCompilerExecutor {
|
|
|
163
167
|
childProcess,
|
|
164
168
|
streamCompletion,
|
|
165
169
|
(r) => (result = r),
|
|
170
|
+
analyzerFindings,
|
|
166
171
|
);
|
|
167
172
|
this.setupStderrHandling(
|
|
168
173
|
childProcess,
|
|
169
174
|
streamCompletion,
|
|
170
175
|
fallbackErrors,
|
|
176
|
+
analyzerFindings,
|
|
171
177
|
);
|
|
172
178
|
this.setupProcessEventHandlers(
|
|
173
179
|
childProcess,
|
|
@@ -219,6 +225,7 @@ export class WorkflowCompilerExecutor {
|
|
|
219
225
|
childProcess: ChildProcess,
|
|
220
226
|
streamCompletion: StreamCompletion,
|
|
221
227
|
onResult: (result: IOutputResult) => void,
|
|
228
|
+
analyzerFindings: Record<string, unknown>[],
|
|
222
229
|
): void {
|
|
223
230
|
if (!childProcess.stdout) {
|
|
224
231
|
streamCompletion.markStdoutEnded();
|
|
@@ -233,13 +240,13 @@ export class WorkflowCompilerExecutor {
|
|
|
233
240
|
buffer = lines.pop()!;
|
|
234
241
|
for (const line of lines) {
|
|
235
242
|
if (!line.trim()) continue;
|
|
236
|
-
this.processOutputLine(line, onResult);
|
|
243
|
+
this.processOutputLine(line, onResult, analyzerFindings);
|
|
237
244
|
}
|
|
238
245
|
});
|
|
239
246
|
|
|
240
247
|
childProcess.stdout.on("end", () => {
|
|
241
248
|
if (buffer.trim()) {
|
|
242
|
-
this.processOutputLine(buffer, onResult);
|
|
249
|
+
this.processOutputLine(buffer, onResult, analyzerFindings);
|
|
243
250
|
}
|
|
244
251
|
streamCompletion.markStdoutEnded();
|
|
245
252
|
});
|
|
@@ -248,6 +255,7 @@ export class WorkflowCompilerExecutor {
|
|
|
248
255
|
private processOutputLine(
|
|
249
256
|
line: string,
|
|
250
257
|
onResult: (result: IOutputResult) => void,
|
|
258
|
+
analyzerFindings: Record<string, unknown>[],
|
|
251
259
|
): void {
|
|
252
260
|
const entry = this.parseOutputEntry(line);
|
|
253
261
|
if (!entry) return;
|
|
@@ -255,7 +263,12 @@ export class WorkflowCompilerExecutor {
|
|
|
255
263
|
if (entry.type === "Result") {
|
|
256
264
|
onResult(entry as IOutputResult);
|
|
257
265
|
} else if (entry.type === "Log") {
|
|
258
|
-
|
|
266
|
+
const log = entry as IOutputLog;
|
|
267
|
+
const finding = toAnalyzerFinding(log);
|
|
268
|
+
if (finding) {
|
|
269
|
+
analyzerFindings.push(finding);
|
|
270
|
+
}
|
|
271
|
+
this.logMessage(log);
|
|
259
272
|
} else if (entry.type === "Progress") {
|
|
260
273
|
this.logProgress(entry as IOutputProgress);
|
|
261
274
|
}
|
|
@@ -285,6 +298,7 @@ export class WorkflowCompilerExecutor {
|
|
|
285
298
|
childProcess: ChildProcess,
|
|
286
299
|
streamCompletion: StreamCompletion,
|
|
287
300
|
fallbackErrors: string[],
|
|
301
|
+
analyzerFindings: Record<string, unknown>[],
|
|
288
302
|
): void {
|
|
289
303
|
if (!childProcess.stderr) {
|
|
290
304
|
streamCompletion.markStderrEnded();
|
|
@@ -298,23 +312,32 @@ export class WorkflowCompilerExecutor {
|
|
|
298
312
|
buffer = lines.pop()!;
|
|
299
313
|
for (const line of lines) {
|
|
300
314
|
if (!line.trim()) continue;
|
|
301
|
-
this.processErrorLine(line, fallbackErrors);
|
|
315
|
+
this.processErrorLine(line, fallbackErrors, analyzerFindings);
|
|
302
316
|
}
|
|
303
317
|
});
|
|
304
318
|
|
|
305
319
|
childProcess.stderr.on("end", () => {
|
|
306
320
|
if (buffer.trim()) {
|
|
307
|
-
this.processErrorLine(buffer, fallbackErrors);
|
|
321
|
+
this.processErrorLine(buffer, fallbackErrors, analyzerFindings);
|
|
308
322
|
}
|
|
309
323
|
streamCompletion.markStderrEnded();
|
|
310
324
|
});
|
|
311
325
|
}
|
|
312
326
|
|
|
313
|
-
private processErrorLine(
|
|
327
|
+
private processErrorLine(
|
|
328
|
+
line: string,
|
|
329
|
+
fallbackErrors: string[],
|
|
330
|
+
analyzerFindings: Record<string, unknown>[],
|
|
331
|
+
): void {
|
|
314
332
|
const entry = this.parseOutputEntry(line);
|
|
315
333
|
|
|
316
334
|
if (entry && entry.type === "Log") {
|
|
317
|
-
|
|
335
|
+
const log = entry as IOutputLog;
|
|
336
|
+
const finding = toAnalyzerFinding(log);
|
|
337
|
+
if (finding) {
|
|
338
|
+
analyzerFindings.push(finding);
|
|
339
|
+
}
|
|
340
|
+
this.logger.error(log.message);
|
|
318
341
|
} else {
|
|
319
342
|
fallbackErrors.push(line);
|
|
320
343
|
this.logger.error(line);
|
|
@@ -434,3 +457,74 @@ function getAdditionalResultDetails(
|
|
|
434
457
|
|
|
435
458
|
return Object.keys(details).length > 0 ? details : undefined;
|
|
436
459
|
}
|
|
460
|
+
|
|
461
|
+
const ANALYZER_LOG_SOURCE = "Analyzer";
|
|
462
|
+
|
|
463
|
+
/** Only a *populated* envelope array wins — `CommandResult` defaults its lists to `[]`. */
|
|
464
|
+
function withAnalyzerFindings(
|
|
465
|
+
details: Record<string, unknown> | undefined,
|
|
466
|
+
findings: Record<string, unknown>[],
|
|
467
|
+
): Record<string, unknown> | undefined {
|
|
468
|
+
const fromEnvelope = details?.findings;
|
|
469
|
+
if (
|
|
470
|
+
findings.length === 0 ||
|
|
471
|
+
(Array.isArray(fromEnvelope) && fromEnvelope.length > 0)
|
|
472
|
+
) {
|
|
473
|
+
return details;
|
|
474
|
+
}
|
|
475
|
+
return { ...details, findings };
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/** Flattened because consumers match flat keys only; the payload omits null members. */
|
|
479
|
+
function toAnalyzerFinding(
|
|
480
|
+
log: IOutputLog,
|
|
481
|
+
): Record<string, unknown> | undefined {
|
|
482
|
+
if (log.source !== ANALYZER_LOG_SOURCE) {
|
|
483
|
+
return undefined;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const message = parseAnalyzerMessage(log.data) ?? {};
|
|
487
|
+
const activityId = message.ActivityId as { IdRef?: string } | undefined;
|
|
488
|
+
const item = message.Item as { Name?: string; Type?: string } | undefined;
|
|
489
|
+
return {
|
|
490
|
+
...message,
|
|
491
|
+
ErrorCode: message.ErrorCode ?? log.code,
|
|
492
|
+
Description: message.Description ?? log.message,
|
|
493
|
+
FilePath: message.FilePath ?? log.filePath,
|
|
494
|
+
ErrorSeverity: message.ErrorSeverity ?? toErrorSeverity(log.logLevel),
|
|
495
|
+
ActivityIdRef: activityId?.IdRef ?? log.id,
|
|
496
|
+
AnalyzerItemName: item?.Name,
|
|
497
|
+
AnalyzerItemType: item?.Type,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/** Undo the compiler's `TraceLevel` → `LogLevel` mapping, so a consumer's lookup still matches. */
|
|
502
|
+
function toErrorSeverity(logLevel: IOutputLog["logLevel"]): string {
|
|
503
|
+
switch (logLevel) {
|
|
504
|
+
case "Debug":
|
|
505
|
+
case "Trace":
|
|
506
|
+
return "Verbose";
|
|
507
|
+
case "Information":
|
|
508
|
+
return "Info";
|
|
509
|
+
default:
|
|
510
|
+
return logLevel;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function parseAnalyzerMessage(
|
|
515
|
+
data: string | undefined,
|
|
516
|
+
): Record<string, unknown> | undefined {
|
|
517
|
+
if (!data) {
|
|
518
|
+
return undefined;
|
|
519
|
+
}
|
|
520
|
+
try {
|
|
521
|
+
const parsed: unknown = JSON.parse(data);
|
|
522
|
+
return typeof parsed === "object" &&
|
|
523
|
+
parsed !== null &&
|
|
524
|
+
!Array.isArray(parsed)
|
|
525
|
+
? (parsed as Record<string, unknown>)
|
|
526
|
+
: undefined;
|
|
527
|
+
} catch {
|
|
528
|
+
return undefined;
|
|
529
|
+
}
|
|
530
|
+
}
|