@uipath/solution-tool 1.204.0-preview.255 → 1.205.0-preview.256
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 +3 -3
- package/dist/THIRD-PARTY-NOTICES.md +2 -2
- package/dist/archive-extract-x35t9ktj.js +120 -0
- package/dist/cleanup-command-service-n4e7vxnw.js +149 -0
- package/dist/deploy-delete-service-cag0q8yx.js +25 -0
- package/dist/deploy-run-service-f0h8wb9w.js +25 -0
- package/dist/{deploy-status-service-6svq7c6w.js → deploy-status-service-yh6xn6e0.js} +41 -33
- package/dist/deploy.js +23 -17
- package/dist/{deployment-search-v2qtz23x.js → deployment-search-mnywd9nc.js} +16 -10
- package/dist/{embedded-file-reader-rf2g1kfj.js → embedded-file-reader-z4becc02.js} +2 -2
- package/dist/{feed-resolver-xz3akamf.js → feed-resolver-fve33dxq.js} +10 -9
- package/dist/feed-scope-failure-jmg4t1r1.js +19 -0
- package/dist/{feed-session-sxga31vn.js → feed-session-7xqphdrg.js} +3 -3
- package/dist/governance-options-4vxz7ekn.js +32 -0
- package/dist/index-g8c8nf1c.js +667 -0
- package/dist/index-prm27bvm.js +138 -0
- package/dist/index.js +27 -29
- package/dist/init.js +11 -10
- package/dist/{list-tjetkspc.js → list-50mepbed.js} +10 -8
- package/dist/models/pack-command-types.d.ts +15 -0
- package/dist/node-7jm7ksqa.js +86 -0
- package/dist/pack-command-service-dewf5vsq.js +35 -0
- package/dist/pack-project-xwd1rbaa.js +97 -0
- package/dist/pack.js +24 -14
- package/dist/package-id-1wr3cy9c.js +13 -0
- package/dist/packager-tool-09swr9cp.js +171 -0
- package/dist/{packager-tool-a9jx8gf4.js → packager-tool-2vmsh5pt.js} +196 -1519
- package/dist/{packager-tool-px1j9kre.js → packager-tool-3bwnkjdq.js} +2 -2
- package/dist/{packager-tool-gb35b4qs.js → packager-tool-43ahv5mn.js} +192 -96
- package/dist/{packager-tool-9ha2z8h5.js → packager-tool-47by487p.js} +6 -4
- package/dist/packager-tool-47wzvqw0.js +86 -0
- package/dist/{packager-tool-zns6n8zp.js → packager-tool-5ddtwzva.js} +1 -1
- package/dist/packager-tool-5edvzz80.js +543 -0
- package/dist/{packager-tool-jq9zw82w.js → packager-tool-5n6jsn41.js} +104 -14
- package/dist/packager-tool-60af1hw2.js +26 -0
- package/dist/{packager-tool-f5gq77m5.js → packager-tool-61pwb567.js} +2 -2
- package/dist/{packager-tool-yrz4mwfn.js → packager-tool-6kz1pv9f.js} +1373 -486
- package/dist/packager-tool-6ybywxxn.js +1809 -0
- package/dist/packager-tool-8ptbmmmp.js +2132 -0
- package/dist/{packager-tool-7gr2nphb.js → packager-tool-97rcgwvv.js} +224 -6
- package/dist/{packager-tool-83m6ns0r.js → packager-tool-9t26kde1.js} +3556 -1646
- package/dist/packager-tool-act5r386.js +239 -0
- package/dist/packager-tool-amhyxzz6.js +125 -0
- package/dist/packager-tool-b03rva43.js +188 -0
- package/dist/{packager-tool-e2rtr49j.js → packager-tool-b2nhscfk.js} +15 -31
- package/dist/{packager-tool-tv9c1e80.js → packager-tool-d6rrfd46.js} +149 -53
- package/dist/packager-tool-frr2xmkr.js +445 -0
- package/dist/{packager-tool-v8bkkqdb.js → packager-tool-gmwva4yv.js} +5 -5
- package/dist/packager-tool-gs3f3tf7.js +103 -0
- package/dist/packager-tool-jg692f13.js +517 -0
- package/dist/packager-tool-jmpb786m.js +109 -0
- package/dist/{packager-tool-5sex4a1h.js → packager-tool-kds8ytzw.js} +74 -23
- package/dist/packager-tool-kkfb5kgn.js +182 -0
- package/dist/packager-tool-kvcxg0zv.js +1510 -0
- package/dist/packager-tool-n3jfjn5r.js +42 -0
- package/dist/{packager-tool-fjsn5h8s.js → packager-tool-n5d5dx5j.js} +2196 -3455
- package/dist/{packager-tool-yfpcvdpm.js → packager-tool-nsya65z1.js} +357 -1370
- package/dist/{packager-tool-fv7xyzg2.js → packager-tool-pbmqk39a.js} +126 -128
- package/dist/{packager-tool-bqre28m1.js → packager-tool-qw47eyw7.js} +1 -1
- package/dist/{packager-tool-9qgmwy21.js → packager-tool-rc3hwn4b.js} +25 -4
- package/dist/packager-tool-resolver-994z3jkj.js +23 -0
- package/dist/{packager-tool-tgw5x6vx.js → packager-tool-rvt68c1t.js} +17 -8
- package/dist/{packager-tool-gab9y5ct.js → packager-tool-t8gh825q.js} +37 -13
- package/dist/{packager-tool-kswwd3hp.js → packager-tool-tx834spe.js} +2376 -4077
- package/dist/{packager-tool-zf3929se.js → packager-tool-v1f5e8t9.js} +11 -1
- package/dist/{packager-tool-ssq2kzqm.js → packager-tool-w77d72a7.js} +13 -11
- package/dist/{packager-tool-1rzssy0x.js → packager-tool-wy4fx250.js} +19 -18
- package/dist/packager-tool-wz34ma3s.js +1333 -0
- package/dist/packager-tool-zq16e1xv.js +68 -0
- package/dist/packager-tool.js +2 -2
- package/dist/prepare-solution-resources-m1djfj62.js +29 -0
- package/dist/project-command-utils-t6pmpvq3.js +90 -0
- package/dist/project-contributions-79ndycrx.js +32 -0
- package/dist/publish.js +13 -12
- package/dist/resource-mutations-c3krjqze.js +36 -0
- package/dist/resource-version-lookup-0e3e2j58.js +99 -0
- package/dist/resource.js +10 -9
- package/dist/restore-command-service-5v0b9363.js +155 -0
- package/dist/run-service-tg6b67wp.js +428 -0
- package/dist/services/deploy-activate-service.d.ts +10 -5
- package/dist/services/deploy-delete-service.d.ts +8 -4
- package/dist/services/deploy-run-service.d.ts +34 -1
- package/dist/services/deploy-uninstall-service.d.ts +21 -3
- package/dist/services/deploy-upgrade-service.d.ts +35 -1
- package/dist/services/deployment-feed-locator.d.ts +15 -4
- package/dist/services/deployment-search-client.d.ts +5 -53
- package/dist/services/deployment-search.d.ts +58 -3
- package/dist/services/deployment-state.d.ts +84 -24
- package/dist/services/deployment-validation.d.ts +5 -0
- package/dist/services/emit-runtime-dependencies.d.ts +2 -1
- package/dist/services/feed-resolver.d.ts +6 -4
- package/dist/services/feed-selection.d.ts +47 -0
- package/dist/services/http-status.d.ts +2 -7
- package/dist/services/pack-command-service.d.ts +9 -0
- package/dist/services/pack-project-selection.d.ts +51 -0
- package/dist/services/pack-project.d.ts +26 -0
- package/dist/services/pack-service.d.ts +7 -3
- package/dist/services/package-search-client.d.ts +7 -0
- package/dist/services/packager-tool-resolver.d.ts +7 -1
- package/dist/services/packages-list-service.d.ts +1 -1
- package/dist/services/permanent-deletion-consent.d.ts +110 -0
- package/dist/services/pipeline-deploy-poll.d.ts +7 -0
- package/dist/services/prepare-solution-resources.d.ts +12 -2
- package/dist/services/project-contributions.d.ts +5 -4
- package/dist/services/search-failure.d.ts +9 -13
- package/dist/services/solution-packager-helpers.d.ts +5 -2
- package/dist/services/solution-path.d.ts +1 -1
- package/dist/services/uninstall-consent.d.ts +82 -0
- package/dist/ship-telemetry-myes7ak5.js +21 -0
- package/dist/solution-auth-aead70sw.js +11 -0
- package/dist/templates/AGENTS.md +125 -12
- package/dist/tool.js +27 -29
- package/dist/utils/deployment-errors.d.ts +9 -1
- package/package.json +2 -2
- package/dist/deploy-delete-service-w3e8x1kq.js +0 -22
- package/dist/feed-scope-failure-0j5byccb.js +0 -18
- package/dist/packager-tool-f2e9rzxk.js +0 -104
- package/dist/packager-tool-resolver-6zwhec62.js +0 -20
- package/dist/packager-tool-zdfn32bc.js +0 -4465
- package/dist/prepare-solution-resources-jpb5as2s.js +0 -25
- package/dist/project-contributions-6wpff2hm.js +0 -30
- package/dist/services/legacy-deployment-search-v3.d.ts +0 -23
|
@@ -9,28 +9,30 @@ import {
|
|
|
9
9
|
} from "./packager-tool-53w8skv5.js";
|
|
10
10
|
import {
|
|
11
11
|
getSolutionAuthContext
|
|
12
|
-
} from "./packager-tool-
|
|
12
|
+
} from "./packager-tool-61pwb567.js";
|
|
13
13
|
import {
|
|
14
14
|
UsersApi,
|
|
15
15
|
createApiClient
|
|
16
|
-
} from "./packager-tool-
|
|
16
|
+
} from "./packager-tool-8ptbmmmp.js";
|
|
17
17
|
import {
|
|
18
18
|
findSolutionFileUpward,
|
|
19
19
|
normalizeProjectType,
|
|
20
20
|
toPortableRelativePath
|
|
21
|
-
} from "./packager-tool-
|
|
21
|
+
} from "./packager-tool-5edvzz80.js";
|
|
22
|
+
import {
|
|
23
|
+
getLoginStatusAsync
|
|
24
|
+
} from "./packager-tool-43ahv5mn.js";
|
|
22
25
|
import {
|
|
23
|
-
catchError,
|
|
24
|
-
extractErrorMessage,
|
|
25
26
|
isGuid,
|
|
26
27
|
logger
|
|
27
|
-
} from "./packager-tool-
|
|
28
|
+
} from "./packager-tool-tx834spe.js";
|
|
28
29
|
import {
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
catchError,
|
|
31
|
+
extractErrorMessage
|
|
32
|
+
} from "./packager-tool-6ybywxxn.js";
|
|
31
33
|
import {
|
|
32
34
|
getFileSystem
|
|
33
|
-
} from "./packager-tool-
|
|
35
|
+
} from "./packager-tool-v1f5e8t9.js";
|
|
34
36
|
import {
|
|
35
37
|
__commonJS,
|
|
36
38
|
__require,
|
|
@@ -2398,103 +2400,6 @@ var require_cjs = __commonJS(function(exports) {
|
|
|
2398
2400
|
return dependency_container_1.instance;
|
|
2399
2401
|
} });
|
|
2400
2402
|
});
|
|
2401
|
-
|
|
2402
|
-
// ../solution-sdk/src/resources/services/solution-manifest.ts
|
|
2403
|
-
async function assertSolutionManifest(dir) {
|
|
2404
|
-
const fs = getFileSystem();
|
|
2405
|
-
const entries = await fs.readdir(dir);
|
|
2406
|
-
const hasManifest = entries.some((f) => f.endsWith(".uipx"));
|
|
2407
|
-
if (!hasManifest) {
|
|
2408
|
-
throw new Error(`${dir} is not a UiPath solution (no .uipx manifest found). Run 'uip solution init' first, or pass --solution-folder pointing at an existing solution.`);
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
async function readSolutionManifest(fs, solutionDir) {
|
|
2412
|
-
const [readdirError, entries] = await catchError(fs.readdir(solutionDir));
|
|
2413
|
-
if (readdirError || !entries)
|
|
2414
|
-
return;
|
|
2415
|
-
const uipx = entries.find((name) => name.endsWith(".uipx"));
|
|
2416
|
-
if (!uipx)
|
|
2417
|
-
return;
|
|
2418
|
-
const uipxPath = fs.path.join(solutionDir, uipx);
|
|
2419
|
-
const [readError, raw] = await catchError(fs.readFile(uipxPath, "utf-8"));
|
|
2420
|
-
if (readError || !raw)
|
|
2421
|
-
return;
|
|
2422
|
-
const [parseError, parsed] = catchError(() => JSON.parse(raw));
|
|
2423
|
-
if (parseError)
|
|
2424
|
-
return;
|
|
2425
|
-
return normalizeManifest(parsed);
|
|
2426
|
-
}
|
|
2427
|
-
function normalizeManifest(parsed) {
|
|
2428
|
-
if (!parsed || typeof parsed !== "object")
|
|
2429
|
-
return {};
|
|
2430
|
-
const raw = parsed.Projects;
|
|
2431
|
-
if (!Array.isArray(raw))
|
|
2432
|
-
return {};
|
|
2433
|
-
const projects = raw.filter((p) => typeof p === "object" && p !== null);
|
|
2434
|
-
return { Projects: projects };
|
|
2435
|
-
}
|
|
2436
|
-
async function resolveProjectDirFromManifest(fs, solutionDir, projectKey) {
|
|
2437
|
-
const manifest = await readSolutionManifest(fs, solutionDir);
|
|
2438
|
-
if (!manifest)
|
|
2439
|
-
return;
|
|
2440
|
-
const entry = manifest.Projects?.find((p) => p.Id === projectKey);
|
|
2441
|
-
const relativePath = entry?.ProjectRelativePath;
|
|
2442
|
-
if (typeof relativePath !== "string" || !relativePath)
|
|
2443
|
-
return;
|
|
2444
|
-
return fs.path.join(solutionDir, fs.path.dirname(toPortableRelativePath(relativePath)));
|
|
2445
|
-
}
|
|
2446
|
-
|
|
2447
|
-
// ../solution-sdk/src/resources/services/entry-point-spec-enhancer.ts
|
|
2448
|
-
var ENTRY_POINTS_FILE = "entry-points.json";
|
|
2449
|
-
|
|
2450
|
-
class EntryPointSpecEnhancer {
|
|
2451
|
-
fs;
|
|
2452
|
-
constructor(fs) {
|
|
2453
|
-
this.fs = fs ?? getFileSystem();
|
|
2454
|
-
}
|
|
2455
|
-
async enhanceSpecAsync(context, resource, _propertiesMetadata, spec, _cancellationToken) {
|
|
2456
|
-
if (!resource.projectKey) {
|
|
2457
|
-
return;
|
|
2458
|
-
}
|
|
2459
|
-
const [enhanceError] = await catchError(this.applyEntryPointAsync(context, resource.projectKey, spec));
|
|
2460
|
-
if (enhanceError) {
|
|
2461
|
-
logger.warn(`EntryPointSpecEnhancer: failed to enhance spec for resource ${resource.key}: ${enhanceError.message}`);
|
|
2462
|
-
}
|
|
2463
|
-
}
|
|
2464
|
-
async applyEntryPointAsync(context, projectKey, spec) {
|
|
2465
|
-
const computed = await computeEntryPointSpec(this.fs, context.path, projectKey);
|
|
2466
|
-
if (!computed)
|
|
2467
|
-
return;
|
|
2468
|
-
for (const [key, value] of Object.entries(computed)) {
|
|
2469
|
-
spec.set(key, value);
|
|
2470
|
-
}
|
|
2471
|
-
}
|
|
2472
|
-
}
|
|
2473
|
-
async function computeEntryPointSpec(fs, solutionDir, projectKey) {
|
|
2474
|
-
const projectDir = await resolveProjectDirFromManifest(fs, solutionDir, projectKey);
|
|
2475
|
-
if (!projectDir)
|
|
2476
|
-
return;
|
|
2477
|
-
const entryPointsPath = fs.path.join(projectDir, ENTRY_POINTS_FILE);
|
|
2478
|
-
if (!await fs.exists(entryPointsPath))
|
|
2479
|
-
return;
|
|
2480
|
-
const entryRaw = await fs.readFile(entryPointsPath, "utf-8");
|
|
2481
|
-
if (!entryRaw)
|
|
2482
|
-
return;
|
|
2483
|
-
const parsed = JSON.parse(entryRaw);
|
|
2484
|
-
const entries = Array.isArray(parsed.entryPoints) ? parsed.entryPoints : [];
|
|
2485
|
-
const only = entries.length === 1 ? entries[0] : undefined;
|
|
2486
|
-
return {
|
|
2487
|
-
entryPoints: entryRaw,
|
|
2488
|
-
entryPointName: only ? only.filePath ?? null : null,
|
|
2489
|
-
entryPointUniqueId: only ? only.uniqueId ?? null : null
|
|
2490
|
-
};
|
|
2491
|
-
}
|
|
2492
|
-
async function applyEntryPointEnhancementAsync(context, resource, spec) {
|
|
2493
|
-
const specMap = new Map(Object.entries(spec));
|
|
2494
|
-
const enhancer = new EntryPointSpecEnhancer;
|
|
2495
|
-
await enhancer.enhanceSpecAsync(context, resource, new Map, specMap);
|
|
2496
|
-
return Object.fromEntries(specMap.entries());
|
|
2497
|
-
}
|
|
2498
2403
|
// ../../node_modules/@uipath/resource-builder-sdk/dist/providers/file-storage-provider.js
|
|
2499
2404
|
function isFileNotFoundError(error) {
|
|
2500
2405
|
if (!error || typeof error != "object")
|
|
@@ -2566,6 +2471,19 @@ class NullSecretsProvider {
|
|
|
2566
2471
|
}
|
|
2567
2472
|
}
|
|
2568
2473
|
|
|
2474
|
+
// ../../node_modules/@uipath/resource-builder-sdk/dist/providers/feature-flag-provider.js
|
|
2475
|
+
class NullFeatureFlagProvider {
|
|
2476
|
+
getBool(_featureFlagName, defaultValue) {
|
|
2477
|
+
return defaultValue ?? false;
|
|
2478
|
+
}
|
|
2479
|
+
getInt(_featureFlagName, defaultValue) {
|
|
2480
|
+
return defaultValue ?? 0;
|
|
2481
|
+
}
|
|
2482
|
+
getString(_featureFlagName, defaultValue) {
|
|
2483
|
+
return defaultValue ?? "";
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2569
2487
|
// ../../node_modules/@uipath/resource-builder-sdk/dist/exceptions/timeout-exception.js
|
|
2570
2488
|
class TimeoutException extends Error {
|
|
2571
2489
|
constructor(message, timeoutMs) {
|
|
@@ -2720,10 +2638,12 @@ function withFileOperationTimeout2(provider, timeoutMs) {
|
|
|
2720
2638
|
// ../../node_modules/@uipath/resource-builder-sdk/dist/solution/solution-context-options.js
|
|
2721
2639
|
var DEFAULT_SOLUTION_CONTEXT_OPTIONS = {
|
|
2722
2640
|
lockTimeoutMs: 30000,
|
|
2723
|
-
fileOperationTimeoutMs: 30000
|
|
2641
|
+
fileOperationTimeoutMs: 30000,
|
|
2642
|
+
maxDegreeOfParallelism: 1
|
|
2724
2643
|
};
|
|
2725
2644
|
var SOLUTION_CONTEXT_TIMEOUT_STORAGE_KEY = "SOL_RB_SOLUTION_CONTEXT_TIMEOUT_MS";
|
|
2726
2645
|
var FILE_OPERATION_TIMEOUT_STORAGE_KEY = "SOL_RB_FILE_OPERATION_TIMEOUT_MS";
|
|
2646
|
+
var MAX_DEGREE_OF_PARALLELISM_STORAGE_KEY = "SOL_RB_MAX_DEGREE_OF_PARALLELISM";
|
|
2727
2647
|
function resolveTimeoutMs(storageKey, configured, fallback) {
|
|
2728
2648
|
const override = readTimeoutOverrideMs(storageKey);
|
|
2729
2649
|
if (override !== undefined)
|
|
@@ -2736,10 +2656,33 @@ function resolveLockTimeoutMs(options) {
|
|
|
2736
2656
|
function resolveFileOperationTimeoutMs(options) {
|
|
2737
2657
|
return resolveTimeoutMs(FILE_OPERATION_TIMEOUT_STORAGE_KEY, options?.fileOperationTimeoutMs, DEFAULT_SOLUTION_CONTEXT_OPTIONS.fileOperationTimeoutMs);
|
|
2738
2658
|
}
|
|
2659
|
+
function isValidDegreeOfParallelism(value) {
|
|
2660
|
+
return typeof value == "number" && Number.isInteger(value) && value > 0;
|
|
2661
|
+
}
|
|
2662
|
+
function resolveMaxDegreeOfParallelism(options) {
|
|
2663
|
+
const override = readDegreeOfParallelismOverride(MAX_DEGREE_OF_PARALLELISM_STORAGE_KEY);
|
|
2664
|
+
if (override !== undefined)
|
|
2665
|
+
return override;
|
|
2666
|
+
return isValidDegreeOfParallelism(options?.maxDegreeOfParallelism) ? options.maxDegreeOfParallelism : DEFAULT_SOLUTION_CONTEXT_OPTIONS.maxDegreeOfParallelism;
|
|
2667
|
+
}
|
|
2668
|
+
function readDegreeOfParallelismOverride(storageKey) {
|
|
2669
|
+
try {
|
|
2670
|
+
if ("u" < typeof localStorage)
|
|
2671
|
+
return;
|
|
2672
|
+
const raw = localStorage.getItem(storageKey);
|
|
2673
|
+
if (raw === null || raw.trim() === "")
|
|
2674
|
+
return;
|
|
2675
|
+
const parsed = Number(raw);
|
|
2676
|
+
return isValidDegreeOfParallelism(parsed) ? parsed : undefined;
|
|
2677
|
+
} catch {
|
|
2678
|
+
return;
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2739
2681
|
function mergeSolutionContextOptions(options) {
|
|
2740
2682
|
return {
|
|
2741
2683
|
lockTimeoutMs: isValidTimeoutMs(options?.lockTimeoutMs) ? options.lockTimeoutMs : DEFAULT_SOLUTION_CONTEXT_OPTIONS.lockTimeoutMs,
|
|
2742
|
-
fileOperationTimeoutMs: isValidTimeoutMs(options?.fileOperationTimeoutMs) ? options.fileOperationTimeoutMs : DEFAULT_SOLUTION_CONTEXT_OPTIONS.fileOperationTimeoutMs
|
|
2684
|
+
fileOperationTimeoutMs: isValidTimeoutMs(options?.fileOperationTimeoutMs) ? options.fileOperationTimeoutMs : DEFAULT_SOLUTION_CONTEXT_OPTIONS.fileOperationTimeoutMs,
|
|
2685
|
+
maxDegreeOfParallelism: isValidDegreeOfParallelism(options?.maxDegreeOfParallelism) ? options.maxDegreeOfParallelism : DEFAULT_SOLUTION_CONTEXT_OPTIONS.maxDegreeOfParallelism
|
|
2743
2686
|
};
|
|
2744
2687
|
}
|
|
2745
2688
|
|
|
@@ -3530,6 +3473,16 @@ function getRootFolder(solution) {
|
|
|
3530
3473
|
}
|
|
3531
3474
|
|
|
3532
3475
|
// ../../node_modules/@uipath/resource-builder-sdk/dist/solution/multi-file-solution-definition-provider.js
|
|
3476
|
+
function nowMs() {
|
|
3477
|
+
return globalThis.performance?.now?.() ?? Date.now();
|
|
3478
|
+
}
|
|
3479
|
+
function round1(ms) {
|
|
3480
|
+
return Math.round(10 * ms) / 10;
|
|
3481
|
+
}
|
|
3482
|
+
function cancellationError(signal) {
|
|
3483
|
+
return signal?.reason instanceof OperationCancelledException ? signal.reason : new OperationCancelledException;
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3533
3486
|
class MultiFileSolutionDefinitionProvider {
|
|
3534
3487
|
get caseInsensitiveSerializer() {
|
|
3535
3488
|
if (!this._caseInsensitiveSerializer)
|
|
@@ -3547,137 +3500,344 @@ class MultiFileSolutionDefinitionProvider {
|
|
|
3547
3500
|
}
|
|
3548
3501
|
async readAsync(context, cancellationToken) {
|
|
3549
3502
|
this.logger.info("Reading multi-file solution definition for solution %s", context.path);
|
|
3550
|
-
const
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
let
|
|
3555
|
-
let
|
|
3556
|
-
|
|
3557
|
-
let
|
|
3558
|
-
|
|
3503
|
+
const loadStarted = nowMs();
|
|
3504
|
+
let fileCount = 0;
|
|
3505
|
+
let listMs = 0;
|
|
3506
|
+
let readStarted = loadStarted;
|
|
3507
|
+
let readWallMs = 0;
|
|
3508
|
+
let readCompleted = false;
|
|
3509
|
+
let ioMs = 0;
|
|
3510
|
+
let maxFileIoMs = 0;
|
|
3511
|
+
let parseMs = 0;
|
|
3512
|
+
let hashMs = 0;
|
|
3513
|
+
let chars = 0;
|
|
3514
|
+
let loadedCount = 0;
|
|
3559
3515
|
const parseErrors = [];
|
|
3560
3516
|
const missingFiles = [];
|
|
3561
3517
|
const invalidFiles = [];
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3518
|
+
let metricsLogged = false;
|
|
3519
|
+
const logReadMetrics = (outcome) => {
|
|
3520
|
+
metricsLogged = true;
|
|
3521
|
+
const totalMs = nowMs() - loadStarted;
|
|
3522
|
+
const mergeMs = readCompleted ? nowMs() - readStarted - readWallMs : 0;
|
|
3523
|
+
const metrics = {
|
|
3524
|
+
outcome,
|
|
3525
|
+
path: context.path,
|
|
3526
|
+
fileCount,
|
|
3527
|
+
totalMs: round1(totalMs),
|
|
3528
|
+
listMs: round1(listMs),
|
|
3529
|
+
readWallMs: round1(readWallMs),
|
|
3530
|
+
ioMs: round1(ioMs),
|
|
3531
|
+
maxFileIoMs: round1(maxFileIoMs),
|
|
3532
|
+
parseMs: round1(parseMs),
|
|
3533
|
+
hashMs: round1(hashMs),
|
|
3534
|
+
mergeMs: round1(mergeMs),
|
|
3535
|
+
chars,
|
|
3536
|
+
degreeOfParallelism: resolveMaxDegreeOfParallelism(this.options),
|
|
3537
|
+
isReadOnly: context.isReadOnly,
|
|
3538
|
+
loaded: loadedCount,
|
|
3539
|
+
missing: missingFiles.length,
|
|
3540
|
+
invalid: invalidFiles.length,
|
|
3541
|
+
parseErrors: parseErrors.length
|
|
3542
|
+
};
|
|
3543
|
+
const message = `Solution read ${outcome} for solution ${context.path}: ${metrics.fileCount} files in ${metrics.totalMs} ms (list ${metrics.listMs} ms, read ${metrics.readWallMs} ms wall, io ${metrics.ioMs} ms summed, max file io ${metrics.maxFileIoMs} ms, parse ${metrics.parseMs} ms, hash ${metrics.hashMs} ms, merge ${metrics.mergeMs} ms); ${metrics.chars} chars; parallelism ${metrics.degreeOfParallelism}; readOnly ${metrics.isReadOnly}; loaded ${metrics.loaded}, missing ${metrics.missing}, invalid ${metrics.invalid}, parseErrors ${metrics.parseErrors}`;
|
|
3544
|
+
if (outcome === "Success")
|
|
3545
|
+
this.logger.info(message, metrics);
|
|
3546
|
+
else
|
|
3547
|
+
this.logger.warn(message, metrics);
|
|
3548
|
+
};
|
|
3549
|
+
const readCore = async () => {
|
|
3550
|
+
const solutionKey = this.getSolutionKey(context);
|
|
3551
|
+
const fileStorageProvider = await this.getFileStorageProviderAsync(context, cancellationToken);
|
|
3552
|
+
const resources = [];
|
|
3553
|
+
const solutionFiles = [];
|
|
3554
|
+
let hashes;
|
|
3555
|
+
let paths;
|
|
3556
|
+
const pathsByKind = this.getPathsByKind(context);
|
|
3557
|
+
let tree;
|
|
3558
|
+
pathsByKind.clear();
|
|
3559
|
+
context.items[SolutionContextKeys.PARSE_ERRORS_ITEM_KEY] = parseErrors;
|
|
3560
|
+
if (!context.isReadOnly) {
|
|
3561
|
+
hashes = this.getHashes(context);
|
|
3562
|
+
paths = this.getPaths(context);
|
|
3563
|
+
tree = this.getTree(context);
|
|
3564
|
+
hashes.clear();
|
|
3565
|
+
paths.clear();
|
|
3566
|
+
tree.length = 0;
|
|
3567
|
+
}
|
|
3568
|
+
const listStarted = nowMs();
|
|
3569
|
+
const resourceFiles = await fileStorageProvider.getFilesAsync(SolutionConstants.RESOURCE_FILES_FOLDER_NAME, "*.json", cancellationToken);
|
|
3570
|
+
listMs = nowMs() - listStarted;
|
|
3571
|
+
fileCount = resourceFiles.length;
|
|
3572
|
+
readStarted = nowMs();
|
|
3573
|
+
const readResults = await this.readResourceFilesAsync(context, fileStorageProvider, resourceFiles, cancellationToken);
|
|
3574
|
+
readWallMs = nowMs() - readStarted;
|
|
3575
|
+
readCompleted = true;
|
|
3576
|
+
let fatal;
|
|
3577
|
+
let hasFatal = false;
|
|
3578
|
+
for (const result of readResults)
|
|
3579
|
+
if (result !== undefined) {
|
|
3580
|
+
if (result.timings) {
|
|
3581
|
+
ioMs += result.timings.ioMs;
|
|
3582
|
+
maxFileIoMs = Math.max(maxFileIoMs, result.timings.ioMs);
|
|
3583
|
+
parseMs += result.timings.parseMs;
|
|
3584
|
+
hashMs += result.timings.hashMs;
|
|
3585
|
+
chars += result.timings.chars;
|
|
3586
|
+
}
|
|
3587
|
+
if (result.fatal !== undefined) {
|
|
3588
|
+
if (!hasFatal) {
|
|
3589
|
+
fatal = result.fatal;
|
|
3590
|
+
hasFatal = true;
|
|
3599
3591
|
}
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3592
|
+
continue;
|
|
3593
|
+
}
|
|
3594
|
+
if (!hasFatal)
|
|
3595
|
+
switch (result.outcome) {
|
|
3596
|
+
case "loaded": {
|
|
3597
|
+
loadedCount++;
|
|
3598
|
+
const rd = result.resource;
|
|
3599
|
+
for (const file of result.files)
|
|
3600
|
+
if (!solutionFiles.some((sf) => sf.name === file.name && sf.kind === file.kind))
|
|
3601
|
+
solutionFiles.push(file);
|
|
3602
|
+
resources.push(rd);
|
|
3603
|
+
const resourceKey = rd.key;
|
|
3604
|
+
const normalizedFullPath = result.normalizedPath;
|
|
3605
|
+
if (!pathsByKind.has(rd.kind))
|
|
3606
|
+
pathsByKind.set(rd.kind, []);
|
|
3607
|
+
const kindPaths = pathsByKind.get(rd.kind);
|
|
3608
|
+
if (kindPaths)
|
|
3609
|
+
kindPaths.push({
|
|
3610
|
+
name: rd.name,
|
|
3611
|
+
key: rd.key,
|
|
3612
|
+
path: normalizedFullPath
|
|
3613
|
+
});
|
|
3614
|
+
if (!context.isReadOnly && hashes && paths) {
|
|
3615
|
+
paths.set(resourceKey, normalizedFullPath);
|
|
3616
|
+
hashes.set(resourceKey, result.hash);
|
|
3617
|
+
}
|
|
3618
|
+
break;
|
|
3604
3619
|
}
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3620
|
+
case "missing":
|
|
3621
|
+
missingFiles.push(result.file);
|
|
3622
|
+
break;
|
|
3623
|
+
case "invalid":
|
|
3624
|
+
invalidFiles.push(result.file);
|
|
3625
|
+
break;
|
|
3626
|
+
case "parseError":
|
|
3627
|
+
parseErrors.push(result.file);
|
|
3628
|
+
break;
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
if (hasFatal) {
|
|
3632
|
+
logReadMetrics("Fatal");
|
|
3633
|
+
throw fatal;
|
|
3634
|
+
}
|
|
3635
|
+
if (missingFiles.length > 0) {
|
|
3636
|
+
logReadMetrics("MissingFiles");
|
|
3637
|
+
throw new BadRequestException(ErrorCodes.Resources.MissingFileDependencies, `Solution has missing files: ${missingFiles.join(", ")}`);
|
|
3638
|
+
}
|
|
3639
|
+
if (invalidFiles.length > 0) {
|
|
3640
|
+
logReadMetrics("InvalidFiles");
|
|
3641
|
+
throw new BadRequestException(ErrorCodes.Resources.InvalidResourceFile, `Solution has resource files with invalid content: ${invalidFiles.join(", ")}`);
|
|
3642
|
+
}
|
|
3643
|
+
if (parseErrors.length > 0) {
|
|
3644
|
+
logReadMetrics("ParseErrors");
|
|
3645
|
+
throw new Error(`Failed to read resource files for solution ${context.path}: ${parseErrors.join(", ")}`);
|
|
3646
|
+
}
|
|
3647
|
+
const folderMap = new Map;
|
|
3648
|
+
const rootFolder = this.buildRootFolder(solutionKey, SolutionConstants.ROOT_FOLDER_NAME);
|
|
3649
|
+
folderMap.set(rootFolder.fullyQualifiedName.toLowerCase(), rootFolder);
|
|
3650
|
+
for (const res of resources)
|
|
3651
|
+
for (const fdep of res.folders ?? []) {
|
|
3652
|
+
const fqnLower = fdep.fullyQualifiedName.toLowerCase();
|
|
3653
|
+
if (!folderMap.has(fqnLower)) {
|
|
3654
|
+
const folderKey = this.generateGuidFromString(fdep.fullyQualifiedName);
|
|
3655
|
+
const pathSegments = fdep.fullyQualifiedName.split("/").filter((s) => s.length > 0).map((p) => {
|
|
3656
|
+
if (p.toLowerCase() !== SolutionConstants.ROOT_FOLDER_NAME.toLowerCase())
|
|
3657
|
+
return this.generateGuidFromString(SolutionConstants.ROOT_FOLDER_NAME);
|
|
3658
|
+
return rootFolder.key;
|
|
3659
|
+
});
|
|
3660
|
+
const folderPath = pathSegments.join(".");
|
|
3661
|
+
folderMap.set(fqnLower, {
|
|
3662
|
+
key: folderKey,
|
|
3663
|
+
fullyQualifiedName: fdep.fullyQualifiedName,
|
|
3664
|
+
path: folderPath
|
|
3617
3665
|
});
|
|
3618
|
-
if (!context.isReadOnly && hashes && paths) {
|
|
3619
|
-
paths.set(resourceKey, normalizedFullPath);
|
|
3620
|
-
hashes.set(resourceKey, await this.computeHash(content));
|
|
3621
3666
|
}
|
|
3622
|
-
} else {
|
|
3623
|
-
this.logger.error("Failed to read resource file %s for solution %s: invalid content", undefined, resourceFile, context.path);
|
|
3624
|
-
invalidFiles.push(resourceFile);
|
|
3625
|
-
}
|
|
3626
|
-
} catch (ex) {
|
|
3627
|
-
if (ex instanceof OperationCancelledException || cancellationToken?.aborted) {
|
|
3628
|
-
this.logger.warn("Operation cancelled while reading file %s for solution %s", resourceFile, context.path);
|
|
3629
|
-
throw ex instanceof OperationCancelledException ? ex : new OperationCancelledException;
|
|
3630
|
-
}
|
|
3631
|
-
if (ex instanceof TimeoutException)
|
|
3632
|
-
throw new TimeoutException(`Reading resource file ${resourceFile} for solution ${context.path} timed out: ${ex.message}`, ex.timeoutMs);
|
|
3633
|
-
if (isFileNotFoundError(ex)) {
|
|
3634
|
-
this.logger.warn("Resource file listed but missing in storage for solution %s: %s", resourceFile, context.path);
|
|
3635
|
-
missingFiles.push(resourceFile);
|
|
3636
|
-
} else {
|
|
3637
|
-
this.logger.error("Failed to read resource file %s for solution %s", ex, resourceFile, context.path);
|
|
3638
|
-
parseErrors.push(resourceFile);
|
|
3639
3667
|
}
|
|
3668
|
+
const solution = {
|
|
3669
|
+
key: solutionKey,
|
|
3670
|
+
docVersion: SolutionConstants.CURRENT_DOC_VERSION,
|
|
3671
|
+
folders: Array.from(folderMap.values()),
|
|
3672
|
+
resources,
|
|
3673
|
+
files: solutionFiles
|
|
3674
|
+
};
|
|
3675
|
+
await this.replaceDuplicateKeysAsync(solution, cancellationToken);
|
|
3676
|
+
if (!context.isReadOnly && tree) {
|
|
3677
|
+
const orderedResourceKeys = getTopologicalIdsOrdering(buildDependencyGraph(solution.resources));
|
|
3678
|
+
tree.push(...orderedResourceKeys);
|
|
3640
3679
|
}
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3680
|
+
logReadMetrics("Success");
|
|
3681
|
+
return solution;
|
|
3682
|
+
};
|
|
3683
|
+
try {
|
|
3684
|
+
return await readCore();
|
|
3685
|
+
} catch (ex) {
|
|
3686
|
+
if (!metricsLogged)
|
|
3687
|
+
logReadMetrics("Fatal");
|
|
3688
|
+
throw ex;
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
async readResourceFilesAsync(context, fileStorageProvider, resourceFiles, cancellationToken) {
|
|
3692
|
+
const results = Array.from({
|
|
3693
|
+
length: resourceFiles.length
|
|
3694
|
+
}, () => {
|
|
3695
|
+
return;
|
|
3696
|
+
});
|
|
3697
|
+
if (resourceFiles.length === 0)
|
|
3698
|
+
return results;
|
|
3699
|
+
const computeHash = !context.isReadOnly;
|
|
3700
|
+
const degreeOfParallelism = Math.min(resolveMaxDegreeOfParallelism(this.options), resourceFiles.length);
|
|
3701
|
+
let nextIndex = 0;
|
|
3702
|
+
let stopRequested = false;
|
|
3703
|
+
const runWorkerAsync = async () => {
|
|
3704
|
+
while (!stopRequested && nextIndex < resourceFiles.length) {
|
|
3705
|
+
const index = nextIndex++;
|
|
3706
|
+
const resourceFile = resourceFiles[index];
|
|
3707
|
+
const result = await this.readResourceFileAsync(context, fileStorageProvider, resourceFile, computeHash, cancellationToken);
|
|
3708
|
+
results[index] = result;
|
|
3709
|
+
if (result.fatal !== undefined)
|
|
3710
|
+
stopRequested = true;
|
|
3667
3711
|
}
|
|
3668
|
-
const solution = {
|
|
3669
|
-
key: solutionKey,
|
|
3670
|
-
docVersion: SolutionConstants.CURRENT_DOC_VERSION,
|
|
3671
|
-
folders: Array.from(folderMap.values()),
|
|
3672
|
-
resources,
|
|
3673
|
-
files: solutionFiles
|
|
3674
3712
|
};
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3713
|
+
const workers = [];
|
|
3714
|
+
for (let i = 0;i < degreeOfParallelism; i++)
|
|
3715
|
+
workers.push(runWorkerAsync());
|
|
3716
|
+
await Promise.all(workers);
|
|
3717
|
+
if (cancellationToken?.aborted && !results.some((result) => result?.fatal !== undefined))
|
|
3718
|
+
throw cancellationError(cancellationToken);
|
|
3719
|
+
return results;
|
|
3720
|
+
}
|
|
3721
|
+
async readResourceFileAsync(context, fileStorageProvider, resourceFile, computeHash, cancellationToken) {
|
|
3722
|
+
const started = nowMs();
|
|
3723
|
+
let ioMs;
|
|
3724
|
+
let content;
|
|
3725
|
+
const failureTimings = () => {
|
|
3726
|
+
const elapsed = nowMs() - started;
|
|
3727
|
+
return ioMs === undefined ? {
|
|
3728
|
+
ioMs: elapsed,
|
|
3729
|
+
parseMs: 0,
|
|
3730
|
+
hashMs: 0,
|
|
3731
|
+
chars: 0
|
|
3732
|
+
} : {
|
|
3733
|
+
ioMs,
|
|
3734
|
+
parseMs: elapsed - ioMs,
|
|
3735
|
+
hashMs: 0,
|
|
3736
|
+
chars: content?.length ?? 0
|
|
3737
|
+
};
|
|
3738
|
+
};
|
|
3739
|
+
try {
|
|
3740
|
+
if (cancellationToken?.aborted)
|
|
3741
|
+
throw cancellationError(cancellationToken);
|
|
3742
|
+
const stream = await fileStorageProvider.openFileAsync(resourceFile, cancellationToken);
|
|
3743
|
+
content = await this.streamToString(stream);
|
|
3744
|
+
ioMs = nowMs() - started;
|
|
3745
|
+
const parseStarted = nowMs();
|
|
3746
|
+
const wrapper = this.caseInsensitiveSerializer.deserialize(content);
|
|
3747
|
+
if (!wrapper?.resource) {
|
|
3748
|
+
this.logger.error("Failed to read resource file %s for solution %s: invalid content", undefined, resourceFile, context.path);
|
|
3749
|
+
return {
|
|
3750
|
+
file: resourceFile,
|
|
3751
|
+
outcome: "invalid",
|
|
3752
|
+
timings: failureTimings()
|
|
3753
|
+
};
|
|
3754
|
+
}
|
|
3755
|
+
const files = [];
|
|
3756
|
+
const rd = this.fromSerializable(wrapper.resource, files);
|
|
3757
|
+
this.applyFolders(rd, resourceFile);
|
|
3758
|
+
const parseMs = nowMs() - parseStarted;
|
|
3759
|
+
let hash;
|
|
3760
|
+
let hashMs = 0;
|
|
3761
|
+
if (computeHash) {
|
|
3762
|
+
const hashStarted = nowMs();
|
|
3763
|
+
hash = await this.computeHash(content);
|
|
3764
|
+
hashMs = nowMs() - hashStarted;
|
|
3765
|
+
}
|
|
3766
|
+
return {
|
|
3767
|
+
file: resourceFile,
|
|
3768
|
+
outcome: "loaded",
|
|
3769
|
+
resource: rd,
|
|
3770
|
+
files,
|
|
3771
|
+
normalizedPath: this.getNormalizedPath(resourceFile),
|
|
3772
|
+
hash,
|
|
3773
|
+
timings: {
|
|
3774
|
+
ioMs,
|
|
3775
|
+
parseMs,
|
|
3776
|
+
hashMs,
|
|
3777
|
+
chars: content.length
|
|
3778
|
+
}
|
|
3779
|
+
};
|
|
3780
|
+
} catch (ex) {
|
|
3781
|
+
if (ex instanceof OperationCancelledException || cancellationToken?.aborted) {
|
|
3782
|
+
this.logger.warn("Operation cancelled while reading file %s for solution %s", resourceFile, context.path);
|
|
3783
|
+
return {
|
|
3784
|
+
file: resourceFile,
|
|
3785
|
+
outcome: "fatal",
|
|
3786
|
+
fatal: ex instanceof OperationCancelledException ? ex : cancellationError(cancellationToken),
|
|
3787
|
+
timings: failureTimings()
|
|
3788
|
+
};
|
|
3789
|
+
}
|
|
3790
|
+
if (ex instanceof TimeoutException)
|
|
3791
|
+
return {
|
|
3792
|
+
file: resourceFile,
|
|
3793
|
+
outcome: "fatal",
|
|
3794
|
+
fatal: new TimeoutException(`Reading resource file ${resourceFile} for solution ${context.path} timed out: ${ex.message}`, ex.timeoutMs),
|
|
3795
|
+
timings: failureTimings()
|
|
3796
|
+
};
|
|
3797
|
+
if (isFileNotFoundError(ex)) {
|
|
3798
|
+
this.logger.warn("Resource file listed but missing in storage for solution %s: %s", resourceFile, context.path);
|
|
3799
|
+
return {
|
|
3800
|
+
file: resourceFile,
|
|
3801
|
+
outcome: "missing",
|
|
3802
|
+
timings: failureTimings()
|
|
3803
|
+
};
|
|
3804
|
+
}
|
|
3805
|
+
this.logger.error("Failed to read resource file %s for solution %s", ex, resourceFile, context.path);
|
|
3806
|
+
return {
|
|
3807
|
+
file: resourceFile,
|
|
3808
|
+
outcome: "parseError",
|
|
3809
|
+
timings: failureTimings()
|
|
3810
|
+
};
|
|
3679
3811
|
}
|
|
3680
|
-
|
|
3812
|
+
}
|
|
3813
|
+
applyFolders(resource, resourceFile) {
|
|
3814
|
+
const relPath = resourceFile.replace(/\\/g, "/");
|
|
3815
|
+
const segments = relPath.split("/").filter((s) => s.length > 0);
|
|
3816
|
+
const firstSegment = segments[0];
|
|
3817
|
+
if (segments.length >= 3 && firstSegment && firstSegment.toLowerCase() === SolutionConstants.RESOURCE_FILES_FOLDER_NAME.toLowerCase()) {
|
|
3818
|
+
const hasType = !!resource.type;
|
|
3819
|
+
const folderSegmentCount = hasType ? segments.length - 3 : segments.length - 2;
|
|
3820
|
+
if (folderSegmentCount < 1)
|
|
3821
|
+
resource.folders = [
|
|
3822
|
+
{
|
|
3823
|
+
fullyQualifiedName: SolutionConstants.ROOT_FOLDER_NAME
|
|
3824
|
+
}
|
|
3825
|
+
];
|
|
3826
|
+
else {
|
|
3827
|
+
const segmentsToUse = segments.slice(1, folderSegmentCount);
|
|
3828
|
+
const folderFqn = segmentsToUse.join("/");
|
|
3829
|
+
resource.folders = [
|
|
3830
|
+
{
|
|
3831
|
+
fullyQualifiedName: folderFqn
|
|
3832
|
+
}
|
|
3833
|
+
];
|
|
3834
|
+
}
|
|
3835
|
+
} else
|
|
3836
|
+
resource.folders = [
|
|
3837
|
+
{
|
|
3838
|
+
fullyQualifiedName: SolutionConstants.ROOT_FOLDER_NAME
|
|
3839
|
+
}
|
|
3840
|
+
];
|
|
3681
3841
|
}
|
|
3682
3842
|
async writeAsync(context, solution, cancellationToken) {
|
|
3683
3843
|
if (context.isReadOnly)
|
|
@@ -3982,7 +4142,7 @@ class MultiFileSolutionDefinitionFactory {
|
|
|
3982
4142
|
// ../../node_modules/@uipath/resource-builder-sdk/dist/embedded-files-default.js
|
|
3983
4143
|
async function createDefaultEmbeddedFileReaderAsync() {
|
|
3984
4144
|
const [{ EmbeddedFileReader }, { JsonSerializerFactory: JsonSerializerFactory2 }] = await Promise.all([
|
|
3985
|
-
import("./embedded-file-reader-
|
|
4145
|
+
import("./embedded-file-reader-z4becc02.js"),
|
|
3986
4146
|
import("./index-dqa169gp.js")
|
|
3987
4147
|
]);
|
|
3988
4148
|
return new EmbeddedFileReader(new JsonSerializerFactory2);
|
|
@@ -4000,6 +4160,7 @@ var ServiceTokens = {
|
|
|
4000
4160
|
SecretsProvider: "ISecretsProvider",
|
|
4001
4161
|
FileStorageProviderFactory: "IFileStorageProviderFactory",
|
|
4002
4162
|
IpRangeBypassSecretProvider: "IIpRangeBypassSecretProvider",
|
|
4163
|
+
FeatureFlagProvider: "IFeatureFlagProvider",
|
|
4003
4164
|
SolutionDefinitionProvider: "ISolutionDefinitionProvider",
|
|
4004
4165
|
SolutionDefinitionFactory: "ISolutionDefinitionFactory",
|
|
4005
4166
|
ArtifactResourceSpecEnhancer: "IArtifactResourceSpecEnhancer",
|
|
@@ -4350,6 +4511,9 @@ function getBindingType(resourceKind, resourceType) {
|
|
|
4350
4511
|
}
|
|
4351
4512
|
|
|
4352
4513
|
// ../../node_modules/@uipath/resource-builder-sdk/dist/bindings/bindings-metadata-serializer.js
|
|
4514
|
+
var SUPPORTED_SCHEMA_MAJOR_VERSION = 2;
|
|
4515
|
+
var INT32_MAX_VALUE = 2147483647;
|
|
4516
|
+
|
|
4353
4517
|
class BindingsMetadataSerializer {
|
|
4354
4518
|
tryDeserialize(input) {
|
|
4355
4519
|
if (!input)
|
|
@@ -4360,30 +4524,52 @@ class BindingsMetadataSerializer {
|
|
|
4360
4524
|
let cleanInput = input.charCodeAt(0) === 65279 ? input.slice(1) : input;
|
|
4361
4525
|
if (cleanInput.charCodeAt(cleanInput.length - 1) === 26)
|
|
4362
4526
|
cleanInput = cleanInput.slice(0, -1);
|
|
4363
|
-
const
|
|
4527
|
+
const root = JSON.parse(cleanInput);
|
|
4528
|
+
if (root === null || typeof root != "object" || Array.isArray(root)) {
|
|
4529
|
+
this.logger.warn("Invalid bindings: content is not a JSON object");
|
|
4530
|
+
return {
|
|
4531
|
+
success: false
|
|
4532
|
+
};
|
|
4533
|
+
}
|
|
4534
|
+
const jsonObject = root;
|
|
4364
4535
|
const versionValue = jsonObject[BindingsKnownKeys.Version];
|
|
4365
|
-
if (
|
|
4536
|
+
if (typeof versionValue != "string") {
|
|
4537
|
+
this.logger.warn(`Invalid bindings: '${BindingsKnownKeys.Version}' is missing or not a string`);
|
|
4538
|
+
return {
|
|
4539
|
+
success: false
|
|
4540
|
+
};
|
|
4541
|
+
}
|
|
4542
|
+
const majorVersion = BindingsMetadataSerializer.tryParseMajorVersion(versionValue);
|
|
4543
|
+
if (majorVersion === undefined) {
|
|
4544
|
+
this.logger.warn(`Invalid bindings: '${BindingsKnownKeys.Version}' is not a valid version`);
|
|
4545
|
+
return {
|
|
4546
|
+
success: false
|
|
4547
|
+
};
|
|
4548
|
+
}
|
|
4549
|
+
if (majorVersion !== SUPPORTED_SCHEMA_MAJOR_VERSION) {
|
|
4550
|
+
this.logger.warn(`Invalid bindings: unsupported schema major version ${majorVersion}`);
|
|
4366
4551
|
return {
|
|
4367
4552
|
success: false
|
|
4368
4553
|
};
|
|
4369
|
-
|
|
4370
|
-
const
|
|
4371
|
-
if (
|
|
4372
|
-
|
|
4554
|
+
}
|
|
4555
|
+
const resources = jsonObject[BindingsKnownKeys.Resources];
|
|
4556
|
+
if (!Array.isArray(resources)) {
|
|
4557
|
+
this.logger.warn(`Invalid bindings: '${BindingsKnownKeys.Resources}' is missing or not an array`);
|
|
4558
|
+
return {
|
|
4559
|
+
success: false
|
|
4560
|
+
};
|
|
4561
|
+
}
|
|
4373
4562
|
const metadata = {
|
|
4374
4563
|
version: versionValue,
|
|
4375
4564
|
resources: []
|
|
4376
4565
|
};
|
|
4377
|
-
const resources
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
this.logger.warn("Skipping null bindings resource element");
|
|
4382
|
-
continue;
|
|
4383
|
-
}
|
|
4384
|
-
const resource = this.getBindingsResourceV2(resourceElement);
|
|
4385
|
-
metadata.resources.push(resource);
|
|
4566
|
+
for (const resourceElement of resources) {
|
|
4567
|
+
if (resourceElement == null) {
|
|
4568
|
+
this.logger.warn("Skipping null bindings resource element");
|
|
4569
|
+
continue;
|
|
4386
4570
|
}
|
|
4571
|
+
metadata.resources.push(this.getBindingsResourceV2(resourceElement));
|
|
4572
|
+
}
|
|
4387
4573
|
this.linkDependencies(metadata.resources);
|
|
4388
4574
|
return {
|
|
4389
4575
|
success: true,
|
|
@@ -4396,6 +4582,22 @@ class BindingsMetadataSerializer {
|
|
|
4396
4582
|
};
|
|
4397
4583
|
}
|
|
4398
4584
|
}
|
|
4585
|
+
static tryParseMajorVersion(value) {
|
|
4586
|
+
const parts = value.split(".");
|
|
4587
|
+
if (parts.length < 2 || parts.length > 4)
|
|
4588
|
+
return;
|
|
4589
|
+
let major;
|
|
4590
|
+
for (const [index, part] of parts.entries()) {
|
|
4591
|
+
if (!/^\s*[+-]?\d+\s*$/.test(part))
|
|
4592
|
+
return;
|
|
4593
|
+
const component = Number(part);
|
|
4594
|
+
if (component < 0 || component > INT32_MAX_VALUE)
|
|
4595
|
+
return;
|
|
4596
|
+
if (index === 0)
|
|
4597
|
+
major = component === 0 ? 0 : component;
|
|
4598
|
+
}
|
|
4599
|
+
return major;
|
|
4600
|
+
}
|
|
4399
4601
|
linkDependencies(resources) {
|
|
4400
4602
|
for (const resource of resources) {
|
|
4401
4603
|
const propertyMeta = resource.metadata;
|
|
@@ -4582,8 +4784,7 @@ class BindingOverwritesHandler {
|
|
|
4582
4784
|
[BindingsKnownKeys.FolderKeyPropertyName]: resourceOverwrite.folderKey ?? ""
|
|
4583
4785
|
};
|
|
4584
4786
|
else {
|
|
4585
|
-
const
|
|
4586
|
-
const folderPath = bindingResource.isArtefactResource && !isBusinessRule ? "" : resourceOverwrite.folderFullyQualifiedName ?? "";
|
|
4787
|
+
const folderPath = bindingResource.isArtefactResource && !bindingResource.isCodedApp ? "" : resourceOverwrite.folderFullyQualifiedName ?? "";
|
|
4587
4788
|
overwrite = {
|
|
4588
4789
|
[BindingsKnownKeys.FolderPathPropertyName]: folderPath,
|
|
4589
4790
|
[BindingsKnownKeys.NamePropertyName]: resourceOverwrite.resourceName
|
|
@@ -4616,6 +4817,11 @@ function removeTimeTriggerBindings(bindings) {
|
|
|
4616
4817
|
}
|
|
4617
4818
|
|
|
4618
4819
|
// ../../node_modules/@uipath/resource-builder-sdk/dist/bindings/binding-overwrites-mapping-handler.js
|
|
4820
|
+
function isCodedAppType(type) {
|
|
4821
|
+
const normalized = type?.toLowerCase();
|
|
4822
|
+
return normalized === "coded" || normalized === "codedaction";
|
|
4823
|
+
}
|
|
4824
|
+
|
|
4619
4825
|
class BindingOverwritesMappingRequestHandler {
|
|
4620
4826
|
async handleAsync(request, cancellationToken) {
|
|
4621
4827
|
const solutionBindings = request.bindings.resources.filter((x) => isSolutionResource(x.metadata));
|
|
@@ -4641,7 +4847,8 @@ class BindingOverwritesMappingRequestHandler {
|
|
|
4641
4847
|
result.push({
|
|
4642
4848
|
binding: bindingsResource,
|
|
4643
4849
|
resourceKey: resource.key,
|
|
4644
|
-
isArtefactResource: resource.projectKey != null
|
|
4850
|
+
isArtefactResource: resource.projectKey != null,
|
|
4851
|
+
isCodedApp: bindingsResource.resourceType === models_BindingsResourceType.App && isCodedAppType(resource.type)
|
|
4645
4852
|
});
|
|
4646
4853
|
} else {
|
|
4647
4854
|
this.logger.error(`Solution does not contain binding resource of type ${models_BindingsResourceType[bindingsResource.resourceType]}.`);
|
|
@@ -5646,6 +5853,9 @@ class SolutionPublishLocationRequest {
|
|
|
5646
5853
|
}
|
|
5647
5854
|
|
|
5648
5855
|
class SolutionPublishLocationRequestV2 {
|
|
5856
|
+
constructor(permissionType) {
|
|
5857
|
+
this.permissionType = permissionType;
|
|
5858
|
+
}
|
|
5649
5859
|
}
|
|
5650
5860
|
|
|
5651
5861
|
class SettingsRequest {
|
|
@@ -5810,12 +6020,13 @@ function _ts_param3(paramIndex, decorator) {
|
|
|
5810
6020
|
}
|
|
5811
6021
|
|
|
5812
6022
|
class SolutionPublishLocationRequestV2Handler {
|
|
5813
|
-
async handleAsync(
|
|
6023
|
+
async handleAsync(request, cancellationToken) {
|
|
5814
6024
|
try {
|
|
5815
6025
|
const orchestratorClient = await this.orchestratorClientFactory.getClientAsync(cancellationToken);
|
|
5816
6026
|
if (!orchestratorClient)
|
|
5817
6027
|
throw new InvalidOperationException("Failed to create Orchestrator client");
|
|
5818
|
-
|
|
6028
|
+
const { permissionType } = request;
|
|
6029
|
+
return permissionType == null ? await orchestratorClient.getPublishLocationsAsync(cancellationToken) : await orchestratorClient.getAccessiblePublishLocationsAsync(permissionType, cancellationToken);
|
|
5819
6030
|
} catch (error) {
|
|
5820
6031
|
this.logger.error("An unexpected error occurred while retrieving publish locations from Automation Solutions service.", error);
|
|
5821
6032
|
throw error;
|
|
@@ -7845,7 +8056,7 @@ class GetResourceKeyForProjectRequestHandler {
|
|
|
7845
8056
|
projectKey: request.projectKey,
|
|
7846
8057
|
solution: request.context.path
|
|
7847
8058
|
});
|
|
7848
|
-
const cacheKey = `prk:${request.projectKey}`;
|
|
8059
|
+
const cacheKey = `prk:${request.context.path}:${request.projectKey}`;
|
|
7849
8060
|
const cached = await this.cacheProvider.getAsync(cacheKey, cancellationToken);
|
|
7850
8061
|
if (cached && typeof cached == "object" && "resourceKey" in cached) {
|
|
7851
8062
|
this.logger.info("Cache hit for project {projectKey} resource key.", {
|
|
@@ -8673,7 +8884,11 @@ class UpdateResourceConfigurationCommandHandler {
|
|
|
8673
8884
|
for (const prop of command.properties) {
|
|
8674
8885
|
const propKey = prop.property.toLowerCase();
|
|
8675
8886
|
const metadata = propertiesMetadata.get(propKey);
|
|
8676
|
-
if (
|
|
8887
|
+
if (metadata && metadata.readOnly !== true) {
|
|
8888
|
+
if (isAnyReferenceProperty(metadata)) {
|
|
8889
|
+
updated = this.setSolutionReference(solution, resource, currentConfiguration, propertiesMetadata, metadata, prop.configuredValue) || updated;
|
|
8890
|
+
continue;
|
|
8891
|
+
}
|
|
8677
8892
|
if (isSecretProperty(metadata)) {
|
|
8678
8893
|
const secretValue = prop.configuredValue?.toString() || "";
|
|
8679
8894
|
const existingKey = currentConfiguration[metadata.property]?.toString();
|
|
@@ -8717,6 +8932,38 @@ class UpdateResourceConfigurationCommandHandler {
|
|
|
8717
8932
|
await command.context.saveChangesAsync(cancellationToken);
|
|
8718
8933
|
return resourceConfiguration;
|
|
8719
8934
|
}
|
|
8935
|
+
setSolutionReference(solution, resource, currentConfiguration, propertiesMetadata, metadata, configuredValue) {
|
|
8936
|
+
const referenceKind = metadata["referenceKind"];
|
|
8937
|
+
if (!isSingleReferenceProperty(metadata) || isFileReferenceProperty(metadata) || typeof referenceKind != "string" || !referenceKind)
|
|
8938
|
+
return false;
|
|
8939
|
+
const requested = asReference(configuredValue);
|
|
8940
|
+
const target = requested?.key ? solution.resources.find((r) => invariantEquals(r.key, requested.key) && invariantEquals(r.kind, referenceKind)) : undefined;
|
|
8941
|
+
if (!requested || !target || invariantEquals(target.key, resource.key))
|
|
8942
|
+
return false;
|
|
8943
|
+
const dependencies = resource.dependencies ??= [];
|
|
8944
|
+
const previous = asReference(currentConfiguration[metadata.property]);
|
|
8945
|
+
const previousKey = previous?.key;
|
|
8946
|
+
const isReferencedElsewhere = (key) => [
|
|
8947
|
+
...propertiesMetadata.values()
|
|
8948
|
+
].some((p) => isAnyReferenceProperty(p) && !isFileReferenceProperty(p) && typeof p["referenceKind"] == "string" && invariantEquals(p["referenceKind"], target.kind) && !invariantEquals(p.property, metadata.property) && asReferences(currentConfiguration[p.property]).some((r) => r.key !== undefined && invariantEquals(r.key, key)));
|
|
8949
|
+
if (previousKey && !invariantEquals(previousKey, target.key) && !isReferencedElsewhere(previousKey)) {
|
|
8950
|
+
const previousName = solution.resources.find((r) => invariantEquals(r.key, previousKey) && invariantEquals(r.kind, target.kind))?.name ?? previous?.name ?? "";
|
|
8951
|
+
resource.dependencies = dependencies.filter((d) => !(invariantEquals(d.kind, target.kind) && invariantEquals(d.name, previousName)));
|
|
8952
|
+
}
|
|
8953
|
+
if (!resource.dependencies.some((d) => invariantEquals(d.kind, target.kind) && invariantEquals(d.name, target.name)))
|
|
8954
|
+
resource.dependencies.push({
|
|
8955
|
+
kind: target.kind,
|
|
8956
|
+
name: target.name
|
|
8957
|
+
});
|
|
8958
|
+
currentConfiguration[metadata.property] = {
|
|
8959
|
+
name: target.name,
|
|
8960
|
+
key: target.key,
|
|
8961
|
+
folderKey: requested.folderKey ?? null,
|
|
8962
|
+
lookupStrategy: null,
|
|
8963
|
+
info: null
|
|
8964
|
+
};
|
|
8965
|
+
return true;
|
|
8966
|
+
}
|
|
8720
8967
|
async updateReferencesName(solutionDefinition, resource, oldName, cancellationToken) {
|
|
8721
8968
|
const resourcesToBeChanged = solutionDefinition.resources.filter((r) => r.key !== resource.key && (r.dependencies ?? []).some((d) => invariantEquals(d.kind, resource.kind) && invariantEquals(d.name, oldName)));
|
|
8722
8969
|
if (resourcesToBeChanged.length === 0)
|
|
@@ -9003,9 +9250,31 @@ class UpsertResourceSecretCommandHandler {
|
|
|
9003
9250
|
this.secretsProvider = secretsProvider;
|
|
9004
9251
|
}
|
|
9005
9252
|
}
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9253
|
+
function asReference(value) {
|
|
9254
|
+
if (!value || typeof value != "object" || Array.isArray(value))
|
|
9255
|
+
return;
|
|
9256
|
+
const record = value;
|
|
9257
|
+
const pick = (name) => {
|
|
9258
|
+
const entry = Object.entries(record).find(([k]) => k.toLowerCase() === name.toLowerCase());
|
|
9259
|
+
return typeof entry?.[1] == "string" ? entry[1] : undefined;
|
|
9260
|
+
};
|
|
9261
|
+
return {
|
|
9262
|
+
key: pick("key"),
|
|
9263
|
+
name: pick("name"),
|
|
9264
|
+
folderKey: pick("folderKey")
|
|
9265
|
+
};
|
|
9266
|
+
}
|
|
9267
|
+
function asReferences(value) {
|
|
9268
|
+
if (Array.isArray(value))
|
|
9269
|
+
return value.map(asReference).filter((r) => r !== undefined);
|
|
9270
|
+
const single = asReference(value);
|
|
9271
|
+
return single ? [
|
|
9272
|
+
single
|
|
9273
|
+
] : [];
|
|
9274
|
+
}
|
|
9275
|
+
|
|
9276
|
+
// ../../node_modules/@uipath/resource-builder-sdk/dist/handlers/solution-handlers.js
|
|
9277
|
+
var DUPLICATE_NAMES_SUPPORTED_KINDS = new Set([
|
|
9009
9278
|
"package"
|
|
9010
9279
|
]);
|
|
9011
9280
|
|
|
@@ -9974,6 +10243,9 @@ class ResourceCatalogApi extends HttpClient {
|
|
|
9974
10243
|
// ../../node_modules/@uipath/resource-builder-sdk/dist/clients/resource-catalog/resource-catalog-client.factory.js
|
|
9975
10244
|
var RESOURCE_CATALOG_SCOPE = "RCS.FolderAuthorization";
|
|
9976
10245
|
var RESOURCE_CATALOG_SERVICE_NAME = "resourcecatalog";
|
|
10246
|
+
function trimTrailingSlashes(url) {
|
|
10247
|
+
return url.replace(/\/+$/, "");
|
|
10248
|
+
}
|
|
9977
10249
|
|
|
9978
10250
|
class ResourceCatalogClientFactory {
|
|
9979
10251
|
async getClientAsync(cancellationToken) {
|
|
@@ -9987,9 +10259,8 @@ class ResourceCatalogClientFactory {
|
|
|
9987
10259
|
], true, cancellationToken);
|
|
9988
10260
|
const ipRangeBypassSecret = await this.ipRangeBypassSecretProvider?.getIpRangeBypassSecretAsync(cancellationToken);
|
|
9989
10261
|
const gatewayIntegrationEnabled = this.resourceBuilderAccessProvider.isGatewayIntegrationEnabled();
|
|
9990
|
-
const normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
|
|
9991
10262
|
const client = new ResourceCatalogApi({
|
|
9992
|
-
baseUrl:
|
|
10263
|
+
baseUrl: trimTrailingSlashes(baseUrl),
|
|
9993
10264
|
securityWorker: (securityData) => {
|
|
9994
10265
|
if (!securityData)
|
|
9995
10266
|
return {};
|
|
@@ -10032,7 +10303,7 @@ class ResourceCatalogClientFactory {
|
|
|
10032
10303
|
if (!accountKey)
|
|
10033
10304
|
throw new Error("Unable to obtain account key");
|
|
10034
10305
|
const client = new ResourceCatalogApi({
|
|
10035
|
-
baseUrl,
|
|
10306
|
+
baseUrl: trimTrailingSlashes(baseUrl),
|
|
10036
10307
|
securityWorker: (securityData) => {
|
|
10037
10308
|
if (!securityData)
|
|
10038
10309
|
return {};
|
|
@@ -12251,7 +12522,16 @@ class AutomationSolutionsClientProxy2 {
|
|
|
12251
12522
|
// ../../node_modules/@uipath/resource-builder-sdk/dist/clients/orchestrator/orchestrator-client.js
|
|
12252
12523
|
class OrchestratorClient {
|
|
12253
12524
|
async getPublishLocationsAsync(cancellationToken) {
|
|
12254
|
-
|
|
12525
|
+
return this.getPublishLocationsFromAsync(this.publishLocationsUrl(), cancellationToken);
|
|
12526
|
+
}
|
|
12527
|
+
async getAccessiblePublishLocationsAsync(permissionType, cancellationToken) {
|
|
12528
|
+
const url = `${this.publishLocationsUrl()}?permissionType=${encodeURIComponent(permissionType)}`;
|
|
12529
|
+
return this.getPublishLocationsFromAsync(url, cancellationToken);
|
|
12530
|
+
}
|
|
12531
|
+
publishLocationsUrl() {
|
|
12532
|
+
return `${this.options.baseUrl}api/v2/AutomationSolutions/publish_locations`;
|
|
12533
|
+
}
|
|
12534
|
+
async getPublishLocationsFromAsync(url, cancellationToken) {
|
|
12255
12535
|
const headers = OrchestratorClientFactory.buildHeaders(this.options.securityData);
|
|
12256
12536
|
const response = await fetch(url, {
|
|
12257
12537
|
method: "GET",
|
|
@@ -12941,7 +13221,7 @@ class ReconcileProjectsCommandHandler {
|
|
|
12941
13221
|
} : {
|
|
12942
13222
|
resourceKey: undefined
|
|
12943
13223
|
};
|
|
12944
|
-
await this.cacheProvider.setAsync(`prk:${projectKey}`, result, cancellationToken);
|
|
13224
|
+
await this.cacheProvider.setAsync(`prk:${context.path}:${projectKey}`, result, cancellationToken);
|
|
12945
13225
|
}
|
|
12946
13226
|
groupByProjectKey(artefactResources) {
|
|
12947
13227
|
const map = new Map;
|
|
@@ -13815,6 +14095,11 @@ class ResourceBuilder {
|
|
|
13815
14095
|
const request = new SolutionPublishLocationRequestV2;
|
|
13816
14096
|
return this._mediator.sendAsync(request, cancellationToken);
|
|
13817
14097
|
}
|
|
14098
|
+
async getAccessiblePublishLocationsAsync(permissionType, cancellationToken) {
|
|
14099
|
+
this.throwIfCancelled(cancellationToken);
|
|
14100
|
+
const request = new SolutionPublishLocationRequestV2(permissionType);
|
|
14101
|
+
return this._mediator.sendAsync(request, cancellationToken);
|
|
14102
|
+
}
|
|
13818
14103
|
async getSettingsAsync(cancellationToken) {
|
|
13819
14104
|
this.throwIfCancelled(cancellationToken);
|
|
13820
14105
|
const request = new SettingsRequest;
|
|
@@ -14374,9 +14659,9 @@ class UnconfiguredEmbeddedFileReader {
|
|
|
14374
14659
|
}
|
|
14375
14660
|
|
|
14376
14661
|
// ../../node_modules/@uipath/resource-builder-sdk/dist/container.js
|
|
14377
|
-
function createContainer(accessProvider, cacheProvider, secretsProvider, fileStorageProviderFactory, ipRangeBypassSecretProvider, solutionDefinitionProvider, solutionDefinitionFactory, artifactResourceSpecEnhancer, httpReaderOptions, loggerFactory, embeddedFileReader, solutionContextOptions) {
|
|
14662
|
+
function createContainer(accessProvider, cacheProvider, secretsProvider, fileStorageProviderFactory, ipRangeBypassSecretProvider, solutionDefinitionProvider, solutionDefinitionFactory, artifactResourceSpecEnhancer, httpReaderOptions, loggerFactory, embeddedFileReader, solutionContextOptions, featureFlagProvider) {
|
|
14378
14663
|
const childContainer = import_tsyringe6.container.createChildContainer();
|
|
14379
|
-
registerProviders(childContainer, accessProvider, cacheProvider, secretsProvider, fileStorageProviderFactory, ipRangeBypassSecretProvider, solutionDefinitionProvider, solutionDefinitionFactory, artifactResourceSpecEnhancer, httpReaderOptions, loggerFactory, solutionContextOptions);
|
|
14664
|
+
registerProviders(childContainer, accessProvider, cacheProvider, secretsProvider, fileStorageProviderFactory, ipRangeBypassSecretProvider, solutionDefinitionProvider, solutionDefinitionFactory, artifactResourceSpecEnhancer, httpReaderOptions, loggerFactory, solutionContextOptions, featureFlagProvider);
|
|
14380
14665
|
registerServices(childContainer, embeddedFileReader);
|
|
14381
14666
|
registerHandlers(childContainer);
|
|
14382
14667
|
childContainer.register(ServiceTokens.Mediator, {
|
|
@@ -14400,11 +14685,15 @@ function registerProvider(diContainer, token, provider) {
|
|
|
14400
14685
|
break;
|
|
14401
14686
|
}
|
|
14402
14687
|
}
|
|
14403
|
-
function registerProviders(diContainer, accessProvider, cacheProvider, secretsProvider, fileStorageProviderFactory, ipRangeBypassSecretProvider, solutionDefinitionProvider, solutionDefinitionFactory, artifactResourceSpecEnhancer, httpReaderOptions, loggerFactory, solutionContextOptions) {
|
|
14688
|
+
function registerProviders(diContainer, accessProvider, cacheProvider, secretsProvider, fileStorageProviderFactory, ipRangeBypassSecretProvider, solutionDefinitionProvider, solutionDefinitionFactory, artifactResourceSpecEnhancer, httpReaderOptions, loggerFactory, solutionContextOptions, featureFlagProvider) {
|
|
14404
14689
|
registerProvider(diContainer, ServiceTokens.AccessProvider, accessProvider);
|
|
14405
14690
|
registerProvider(diContainer, ServiceTokens.CacheProvider, cacheProvider);
|
|
14406
14691
|
registerProvider(diContainer, ServiceTokens.SecretsProvider, secretsProvider);
|
|
14407
14692
|
registerProvider(diContainer, ServiceTokens.IpRangeBypassSecretProvider, ipRangeBypassSecretProvider);
|
|
14693
|
+
registerProvider(diContainer, ServiceTokens.FeatureFlagProvider, featureFlagProvider ?? {
|
|
14694
|
+
instance: new NullFeatureFlagProvider,
|
|
14695
|
+
lifetime: service_tokens_ServiceLifetime.Singleton
|
|
14696
|
+
});
|
|
14408
14697
|
registerProvider(diContainer, ServiceTokens.FileStorageProviderFactory, fileStorageProviderFactory);
|
|
14409
14698
|
registerProvider(diContainer, ServiceTokens.SolutionDefinitionProvider, solutionDefinitionProvider);
|
|
14410
14699
|
registerProvider(diContainer, ServiceTokens.SolutionDefinitionFactory, solutionDefinitionFactory);
|
|
@@ -15131,6 +15420,11 @@ class ResourceBuilderConfiguration {
|
|
|
15131
15420
|
this._ipRangeBypassSecretProviderLifetime = lifetime;
|
|
15132
15421
|
return this;
|
|
15133
15422
|
}
|
|
15423
|
+
addFeatureFlagProvider(provider, lifetime = service_tokens_ServiceLifetime.Scoped) {
|
|
15424
|
+
this._featureFlagProviderProvider = provider;
|
|
15425
|
+
this._featureFlagProviderLifetime = lifetime;
|
|
15426
|
+
return this;
|
|
15427
|
+
}
|
|
15134
15428
|
addSolutionDefinitionProvider(provider, lifetime = service_tokens_ServiceLifetime.Scoped) {
|
|
15135
15429
|
this._solutionDefinitionProviderProvider = provider;
|
|
15136
15430
|
this._solutionDefinitionProviderLifetime = lifetime;
|
|
@@ -15184,6 +15478,11 @@ class ResourceBuilderConfiguration {
|
|
|
15184
15478
|
instance: ipRangeBypassSecretProviderInstance,
|
|
15185
15479
|
lifetime: this._ipRangeBypassSecretProviderLifetime
|
|
15186
15480
|
};
|
|
15481
|
+
const featureFlagProviderInstance = this._featureFlagProviderProvider ? await resolveProvider(this._featureFlagProviderProvider) : new NullFeatureFlagProvider;
|
|
15482
|
+
const featureFlagProvider = {
|
|
15483
|
+
instance: featureFlagProviderInstance,
|
|
15484
|
+
lifetime: this._featureFlagProviderLifetime
|
|
15485
|
+
};
|
|
15187
15486
|
const secretsProviderInstance = this._secretsProviderProvider ? await resolveProvider(this._secretsProviderProvider) : new NullSecretsProvider;
|
|
15188
15487
|
const secretsProvider = {
|
|
15189
15488
|
instance: secretsProviderInstance,
|
|
@@ -15227,7 +15526,7 @@ class ResourceBuilderConfiguration {
|
|
|
15227
15526
|
lifetime: this._artifactResourceSpecEnhancerLifetime
|
|
15228
15527
|
};
|
|
15229
15528
|
const embeddedFileReader = await createDefaultEmbeddedFileReaderAsync();
|
|
15230
|
-
const container3 = createContainer(accessProvider, cacheProvider, secretsProvider, fileStorageProviderFactory, ipRangeBypassSecretProvider, solutionDefinitionProvider, solutionDefinitionFactory, artifactResourceSpecEnhancer, this._httpReaderOptions, loggerFactory, embeddedFileReader, this._solutionContextOptions);
|
|
15529
|
+
const container3 = createContainer(accessProvider, cacheProvider, secretsProvider, fileStorageProviderFactory, ipRangeBypassSecretProvider, solutionDefinitionProvider, solutionDefinitionFactory, artifactResourceSpecEnhancer, this._httpReaderOptions, loggerFactory, embeddedFileReader, this._solutionContextOptions, featureFlagProvider);
|
|
15231
15530
|
return new ResourceBuilderServices(container3);
|
|
15232
15531
|
}
|
|
15233
15532
|
async buildServices() {
|
|
@@ -15239,6 +15538,7 @@ class ResourceBuilderConfiguration {
|
|
|
15239
15538
|
this._secretsProviderLifetime = service_tokens_ServiceLifetime.Scoped;
|
|
15240
15539
|
this._fileStorageProviderFactoryLifetime = service_tokens_ServiceLifetime.Singleton;
|
|
15241
15540
|
this._ipRangeBypassSecretProviderLifetime = service_tokens_ServiceLifetime.Scoped;
|
|
15541
|
+
this._featureFlagProviderLifetime = service_tokens_ServiceLifetime.Scoped;
|
|
15242
15542
|
this._solutionDefinitionProviderLifetime = service_tokens_ServiceLifetime.Scoped;
|
|
15243
15543
|
this._solutionDefinitionFactoryLifetime = service_tokens_ServiceLifetime.Scoped;
|
|
15244
15544
|
this._artifactResourceSpecEnhancerLifetime = service_tokens_ServiceLifetime.Scoped;
|
|
@@ -15248,13 +15548,161 @@ class ResourceBuilderConfiguration {
|
|
|
15248
15548
|
function createResourceBuilderConfiguration() {
|
|
15249
15549
|
return new ResourceBuilderConfiguration;
|
|
15250
15550
|
}
|
|
15551
|
+
// ../solution-sdk/src/resources/services/solution-manifest.ts
|
|
15552
|
+
async function assertSolutionManifest(dir) {
|
|
15553
|
+
const fs = getFileSystem();
|
|
15554
|
+
const entries = await fs.readdir(dir);
|
|
15555
|
+
const hasManifest = entries.some((f) => f.endsWith(".uipx"));
|
|
15556
|
+
if (!hasManifest) {
|
|
15557
|
+
throw new Error(`${dir} is not a UiPath solution (no .uipx manifest found). Run 'uip solution init' first, or pass --solution-folder pointing at an existing solution.`);
|
|
15558
|
+
}
|
|
15559
|
+
}
|
|
15560
|
+
async function readSolutionManifest(fs, solutionDir) {
|
|
15561
|
+
const [readdirError, entries] = await catchError(fs.readdir(solutionDir));
|
|
15562
|
+
if (readdirError || !entries)
|
|
15563
|
+
return;
|
|
15564
|
+
const uipx = entries.find((name) => name.endsWith(".uipx"));
|
|
15565
|
+
if (!uipx)
|
|
15566
|
+
return;
|
|
15567
|
+
const uipxPath = fs.path.join(solutionDir, uipx);
|
|
15568
|
+
const [readError, raw] = await catchError(fs.readFile(uipxPath, "utf-8"));
|
|
15569
|
+
if (readError || !raw)
|
|
15570
|
+
return;
|
|
15571
|
+
const [parseError, parsed] = catchError(() => JSON.parse(raw));
|
|
15572
|
+
if (parseError)
|
|
15573
|
+
return;
|
|
15574
|
+
return normalizeManifest(parsed);
|
|
15575
|
+
}
|
|
15576
|
+
function normalizeManifest(parsed) {
|
|
15577
|
+
if (!parsed || typeof parsed !== "object")
|
|
15578
|
+
return {};
|
|
15579
|
+
const raw = parsed.Projects;
|
|
15580
|
+
if (!Array.isArray(raw))
|
|
15581
|
+
return {};
|
|
15582
|
+
const projects = raw.filter((p) => typeof p === "object" && p !== null);
|
|
15583
|
+
return { Projects: projects };
|
|
15584
|
+
}
|
|
15585
|
+
async function resolveProjectDirFromManifest(fs, solutionDir, projectKey) {
|
|
15586
|
+
const manifest = await readSolutionManifest(fs, solutionDir);
|
|
15587
|
+
if (!manifest)
|
|
15588
|
+
return;
|
|
15589
|
+
const entry = manifest.Projects?.find((p) => p.Id === projectKey);
|
|
15590
|
+
const relativePath = entry?.ProjectRelativePath;
|
|
15591
|
+
if (typeof relativePath !== "string" || !relativePath)
|
|
15592
|
+
return;
|
|
15593
|
+
return fs.path.join(solutionDir, fs.path.dirname(toPortableRelativePath(relativePath)));
|
|
15594
|
+
}
|
|
15595
|
+
|
|
15596
|
+
// ../solution-sdk/src/resources/services/entry-point-spec-enhancer.ts
|
|
15597
|
+
var ENTRY_POINTS_FILE = "entry-points.json";
|
|
15598
|
+
|
|
15599
|
+
class EntryPointSpecEnhancer {
|
|
15600
|
+
fs;
|
|
15601
|
+
constructor(fs) {
|
|
15602
|
+
this.fs = fs ?? getFileSystem();
|
|
15603
|
+
}
|
|
15604
|
+
async enhanceSpecAsync(context, resource, _propertiesMetadata, spec, _cancellationToken) {
|
|
15605
|
+
if (!resource.projectKey) {
|
|
15606
|
+
return;
|
|
15607
|
+
}
|
|
15608
|
+
const [enhanceError] = await catchError(this.applyEntryPointAsync(context, resource.projectKey, spec));
|
|
15609
|
+
if (enhanceError) {
|
|
15610
|
+
logger.warn(`EntryPointSpecEnhancer: failed to enhance spec for resource ${resource.key}: ${enhanceError.message}`);
|
|
15611
|
+
}
|
|
15612
|
+
}
|
|
15613
|
+
async applyEntryPointAsync(context, projectKey, spec) {
|
|
15614
|
+
const computed = await computeEntryPointSpec(this.fs, context.path, projectKey);
|
|
15615
|
+
if (!computed)
|
|
15616
|
+
return;
|
|
15617
|
+
for (const [key, value] of Object.entries(computed)) {
|
|
15618
|
+
spec.set(key, value);
|
|
15619
|
+
}
|
|
15620
|
+
}
|
|
15621
|
+
}
|
|
15622
|
+
async function computeEntryPointSpec(fs, solutionDir, projectKey) {
|
|
15623
|
+
const projectDir = await resolveProjectDirFromManifest(fs, solutionDir, projectKey);
|
|
15624
|
+
if (!projectDir)
|
|
15625
|
+
return;
|
|
15626
|
+
const entryPointsPath = fs.path.join(projectDir, ENTRY_POINTS_FILE);
|
|
15627
|
+
if (!await fs.exists(entryPointsPath))
|
|
15628
|
+
return;
|
|
15629
|
+
const entryRaw = await fs.readFile(entryPointsPath, "utf-8");
|
|
15630
|
+
if (!entryRaw)
|
|
15631
|
+
return;
|
|
15632
|
+
const parsed = JSON.parse(entryRaw);
|
|
15633
|
+
const entries = Array.isArray(parsed.entryPoints) ? parsed.entryPoints : [];
|
|
15634
|
+
const only = entries.length === 1 ? entries[0] : undefined;
|
|
15635
|
+
return {
|
|
15636
|
+
entryPoints: entryRaw,
|
|
15637
|
+
entryPointName: only ? only.filePath ?? null : null,
|
|
15638
|
+
entryPointUniqueId: only ? only.uniqueId ?? null : null
|
|
15639
|
+
};
|
|
15640
|
+
}
|
|
15641
|
+
async function applyEntryPointEnhancementAsync(context, resource, spec) {
|
|
15642
|
+
const specMap = new Map(Object.entries(spec));
|
|
15643
|
+
const enhancer = new EntryPointSpecEnhancer;
|
|
15644
|
+
await enhancer.enhanceSpecAsync(context, resource, new Map, specMap);
|
|
15645
|
+
return Object.fromEntries(specMap.entries());
|
|
15646
|
+
}
|
|
15647
|
+
|
|
15251
15648
|
// ../solution-sdk/src/resources/providers/artefact-pair-binding-provider.ts
|
|
15252
15649
|
var ARTEFACT_KIND_APP = "app";
|
|
15253
15650
|
var ARTEFACT_KIND_APP_VERSION = "appversion";
|
|
15254
15651
|
var ARTEFACT_KIND_PACKAGE = "package";
|
|
15255
15652
|
var SPEC_KEY_PACKAGE = "package";
|
|
15653
|
+
var RESOURCE_FILES_FOLDER_NAME = "resources";
|
|
15654
|
+
var RESOURCE_FILE_PATTERN = "*.json";
|
|
15655
|
+
var FIX_SHARED_KEY = "Delete all but one file for each key, then retry.";
|
|
15656
|
+
var FIX_KEYLESS = 'Give each file without a key its own unique "key", then retry.';
|
|
15657
|
+
|
|
15658
|
+
class DuplicateKeysError extends Error {
|
|
15659
|
+
keys;
|
|
15660
|
+
constructor(keys) {
|
|
15661
|
+
super(`Duplicate resource keys: ${keys.map(describeKey).join(", ")}`);
|
|
15662
|
+
this.keys = keys;
|
|
15663
|
+
}
|
|
15664
|
+
}
|
|
15256
15665
|
|
|
15257
15666
|
class ArtefactPairBindingProvider extends MultiFileSolutionDefinitionProvider {
|
|
15667
|
+
async readAsync(context, cancellationToken) {
|
|
15668
|
+
const [readError, solution] = await catchError(super.readAsync(context, cancellationToken));
|
|
15669
|
+
if (!readError)
|
|
15670
|
+
return solution;
|
|
15671
|
+
if (!(readError instanceof DuplicateKeysError))
|
|
15672
|
+
throw readError;
|
|
15673
|
+
const [describeError, message] = await catchError(this.describeDuplicateKeys(context, readError.keys, cancellationToken));
|
|
15674
|
+
throw describeError ? readError : new Error(message);
|
|
15675
|
+
}
|
|
15676
|
+
async replaceDuplicateKeysAsync(solution) {
|
|
15677
|
+
const keys = findDuplicateKeys(solution);
|
|
15678
|
+
if (keys.length > 0)
|
|
15679
|
+
throw new DuplicateKeysError(keys);
|
|
15680
|
+
}
|
|
15681
|
+
async describeDuplicateKeys(context, keys, cancellationToken) {
|
|
15682
|
+
const storage = await this.getFileStorageProviderAsync(context, cancellationToken);
|
|
15683
|
+
const files = await storage.getFilesAsync(RESOURCE_FILES_FOLDER_NAME, RESOURCE_FILE_PATTERN, cancellationToken);
|
|
15684
|
+
const pathsByKey = new Map(keys.map((key) => [key, []]));
|
|
15685
|
+
for (const file of [...files].sort()) {
|
|
15686
|
+
const [readError, key] = await catchError(this.readResourceKey(storage, file, cancellationToken));
|
|
15687
|
+
if (readError)
|
|
15688
|
+
continue;
|
|
15689
|
+
pathsByKey.get(key)?.push(toPortableRelativePath(file));
|
|
15690
|
+
}
|
|
15691
|
+
const lines = [...pathsByKey].map(([key, paths]) => `Resource key ${describeKey(key)} is used by: ${paths.join(", ")}`);
|
|
15692
|
+
const fixes = [
|
|
15693
|
+
keys.some((key) => !isKeyless(key)) && FIX_SHARED_KEY,
|
|
15694
|
+
keys.some(isKeyless) && FIX_KEYLESS
|
|
15695
|
+
].filter(Boolean);
|
|
15696
|
+
return [...lines, ...fixes].join(`
|
|
15697
|
+
`);
|
|
15698
|
+
}
|
|
15699
|
+
async readResourceKey(storage, file, cancellationToken) {
|
|
15700
|
+
const content = await storage.readFileToEndAsync(file, cancellationToken);
|
|
15701
|
+
const wrapper = this.caseInsensitiveSerializer.deserialize(content);
|
|
15702
|
+
if (!wrapper?.resource)
|
|
15703
|
+
throw new Error(`No resource in ${file}`);
|
|
15704
|
+
return wrapper.resource.key;
|
|
15705
|
+
}
|
|
15258
15706
|
async writeAsync(context, solution, cancellationToken) {
|
|
15259
15707
|
patchAppPackageLinkage(solution);
|
|
15260
15708
|
return super.writeAsync(context, solution, cancellationToken);
|
|
@@ -15266,6 +15714,22 @@ class ArtefactPairBindingSolutionDefinitionFactory extends MultiFileSolutionDefi
|
|
|
15266
15714
|
return new ArtefactPairBindingProvider(this.fileStorageProviderFactory, this.jsonSerializerFactory, this.loggerFactory);
|
|
15267
15715
|
}
|
|
15268
15716
|
}
|
|
15717
|
+
function findDuplicateKeys(solution) {
|
|
15718
|
+
const counts = new Map;
|
|
15719
|
+
for (const resource of solution.resources ?? []) {
|
|
15720
|
+
const key = resource.key;
|
|
15721
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
15722
|
+
}
|
|
15723
|
+
return [...counts].filter(([, count]) => count > 1).map(([key]) => key);
|
|
15724
|
+
}
|
|
15725
|
+
function isKeyless(key) {
|
|
15726
|
+
return typeof key !== "string" || !key;
|
|
15727
|
+
}
|
|
15728
|
+
function describeKey(key) {
|
|
15729
|
+
if (typeof key === "string" && key)
|
|
15730
|
+
return key;
|
|
15731
|
+
return key === undefined ? "(missing)" : JSON.stringify(key);
|
|
15732
|
+
}
|
|
15269
15733
|
function patchAppPackageLinkage(solution) {
|
|
15270
15734
|
const resources = solution.resources ?? [];
|
|
15271
15735
|
const projectKeys = new Set;
|
|
@@ -15737,30 +16201,107 @@ async function createProjectInSolutionBuilder(solutionDir, projectId, projectNam
|
|
|
15737
16201
|
await catchError(solutionBuilder.disposeAsync());
|
|
15738
16202
|
return createError;
|
|
15739
16203
|
}
|
|
15740
|
-
|
|
15741
|
-
|
|
15742
|
-
|
|
15743
|
-
const
|
|
15744
|
-
|
|
16204
|
+
// ../solution-sdk/src/resources/services/catalog-lookup-failure.ts
|
|
16205
|
+
var NO_CLIENT_MESSAGE = "Unable to create Resource Catalog client";
|
|
16206
|
+
function classifyCatalogFailure(error) {
|
|
16207
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16208
|
+
if (message.includes(NO_CLIENT_MESSAGE)) {
|
|
16209
|
+
return { message, kind: "no_session" };
|
|
16210
|
+
}
|
|
16211
|
+
const match = /\bHTTP (\d{3})\b/.exec(message);
|
|
16212
|
+
const status = match ? Number(match[1]) : undefined;
|
|
16213
|
+
if (status === 401 || status === 403) {
|
|
16214
|
+
return { message, kind: "refused", status };
|
|
16215
|
+
}
|
|
16216
|
+
return { message, kind: "failed", ...status ? { status } : {} };
|
|
15745
16217
|
}
|
|
15746
|
-
function
|
|
15747
|
-
|
|
15748
|
-
return { Created: false, Error: error.message };
|
|
16218
|
+
function stopsCatalogLookups(failure) {
|
|
16219
|
+
return failure.kind !== "failed";
|
|
15749
16220
|
}
|
|
15750
|
-
|
|
15751
|
-
|
|
15752
|
-
var reader;
|
|
15753
|
-
function hasBareTemplateRow(projectType) {
|
|
15754
|
-
reader ??= new EmbeddedProjectConfigurationReader;
|
|
15755
|
-
return reader.getTemplateResourcesByType(projectType, undefined, false).length > 0;
|
|
16221
|
+
function describeBindings(bindings) {
|
|
16222
|
+
return bindings.map((b) => `${b.kind} "${b.name}"`).join(", ");
|
|
15756
16223
|
}
|
|
15757
|
-
function
|
|
15758
|
-
|
|
15759
|
-
|
|
15760
|
-
|
|
15761
|
-
|
|
15762
|
-
|
|
15763
|
-
|
|
16224
|
+
function describeCause(failure) {
|
|
16225
|
+
switch (failure.kind) {
|
|
16226
|
+
case "no_session":
|
|
16227
|
+
return "There is no login, so the Resource Catalog could not be searched";
|
|
16228
|
+
case "refused":
|
|
16229
|
+
return `The Resource Catalog refused this login (${failure.message})`;
|
|
16230
|
+
default:
|
|
16231
|
+
return `A Resource Catalog call failed (${failure.message})`;
|
|
16232
|
+
}
|
|
16233
|
+
}
|
|
16234
|
+
function classify(failure) {
|
|
16235
|
+
const refreshAndCommit = "or run 'uip solution resources refresh' under a login the Resource Catalog accepts and commit the solution's resources. The resources already in the solution are used as they are.";
|
|
16236
|
+
if (failure.kind === "no_session") {
|
|
16237
|
+
return {
|
|
16238
|
+
errorCode: "authentication_required",
|
|
16239
|
+
retry: "RetryWillNotFix",
|
|
16240
|
+
instructions: `Run 'uip login', then re-run; ${refreshAndCommit}`
|
|
16241
|
+
};
|
|
16242
|
+
}
|
|
16243
|
+
if (failure.kind === "refused") {
|
|
16244
|
+
return {
|
|
16245
|
+
errorCode: "permission_denied",
|
|
16246
|
+
retry: "RetryWillNotFix",
|
|
16247
|
+
instructions: `Use a login the Resource Catalog accepts (a user login: 'uip login'), ${refreshAndCommit}`
|
|
16248
|
+
};
|
|
16249
|
+
}
|
|
16250
|
+
const status = failure.status;
|
|
16251
|
+
if (status === 429) {
|
|
16252
|
+
return {
|
|
16253
|
+
errorCode: "rate_limited",
|
|
16254
|
+
retry: "RetryLater",
|
|
16255
|
+
instructions: `Wait, then re-run; ${refreshAndCommit}`
|
|
16256
|
+
};
|
|
16257
|
+
}
|
|
16258
|
+
if (status !== undefined && status >= 500) {
|
|
16259
|
+
return {
|
|
16260
|
+
errorCode: "server_error",
|
|
16261
|
+
retry: "RetryLater",
|
|
16262
|
+
instructions: `Retry once the Resource Catalog is back; ${refreshAndCommit}`
|
|
16263
|
+
};
|
|
16264
|
+
}
|
|
16265
|
+
if (status !== undefined && status >= 400) {
|
|
16266
|
+
return {
|
|
16267
|
+
errorCode: "unknown_error",
|
|
16268
|
+
retry: "RetryWillNotFix",
|
|
16269
|
+
instructions: `The Resource Catalog rejected the lookup for these bindings, so a retry gets the same answer. Check them in the project's bindings, ${refreshAndCommit}`
|
|
16270
|
+
};
|
|
16271
|
+
}
|
|
16272
|
+
return {
|
|
16273
|
+
errorCode: "unknown_error",
|
|
16274
|
+
retry: "RetryLater",
|
|
16275
|
+
instructions: `Retry; if it keeps failing, ${refreshAndCommit}`
|
|
16276
|
+
};
|
|
16277
|
+
}
|
|
16278
|
+
function catalogLookupWarning(failure, kept, unresolved) {
|
|
16279
|
+
const parts = [
|
|
16280
|
+
`${describeCause(failure)}, so ${kept + unresolved.length} binding(s) were not checked against the cloud.`
|
|
16281
|
+
];
|
|
16282
|
+
if (kept > 0) {
|
|
16283
|
+
parts.push(`${kept} already in the solution were kept as they are.`);
|
|
16284
|
+
}
|
|
16285
|
+
if (unresolved.length > 0) {
|
|
16286
|
+
parts.push(`Not added to the solution: ${describeBindings(unresolved)}.`);
|
|
16287
|
+
}
|
|
16288
|
+
return parts.join(" ");
|
|
16289
|
+
}
|
|
16290
|
+
|
|
16291
|
+
class CatalogLookupError extends Error {
|
|
16292
|
+
errorCode;
|
|
16293
|
+
retry;
|
|
16294
|
+
instructions;
|
|
16295
|
+
unresolved;
|
|
16296
|
+
constructor(failure, unresolved) {
|
|
16297
|
+
super(`${describeCause(failure)}, so these bindings could not be resolved and are not in the solution: ${describeBindings(unresolved)}.`);
|
|
16298
|
+
this.name = "CatalogLookupError";
|
|
16299
|
+
const { errorCode, retry, instructions } = classify(failure);
|
|
16300
|
+
this.errorCode = errorCode;
|
|
16301
|
+
this.retry = retry;
|
|
16302
|
+
this.instructions = instructions;
|
|
16303
|
+
this.unresolved = unresolved;
|
|
16304
|
+
}
|
|
15764
16305
|
}
|
|
15765
16306
|
// ../solution-sdk/src/resources/services/local-resource-matcher.ts
|
|
15766
16307
|
function normalizeFolderPath(path) {
|
|
@@ -15821,71 +16362,182 @@ function findMemberCloneResources(resources, memberProjectKeys) {
|
|
|
15821
16362
|
function hasPackageDependency(resource) {
|
|
15822
16363
|
return (resource.dependencies ?? []).some((dependency) => (dependency.kind ?? "").toLowerCase() === "package");
|
|
15823
16364
|
}
|
|
15824
|
-
|
|
15825
|
-
|
|
15826
|
-
|
|
15827
|
-
|
|
15828
|
-
return value ? value.charAt(0).toLowerCase() + value.slice(1) : value;
|
|
16365
|
+
// ../solution-sdk/src/resources/services/project-artifacts-service.ts
|
|
16366
|
+
async function addProjectArtifactsToSolutionAsync(options) {
|
|
16367
|
+
const error = await createProjectInSolutionBuilder(options.solutionDir, options.projectId, options.projectName, normalizeProjectType(options.projectType), options.projectSubType);
|
|
16368
|
+
return error ? artifactFailure(options, error) : { Created: true };
|
|
15829
16369
|
}
|
|
15830
|
-
function
|
|
15831
|
-
|
|
15832
|
-
|
|
15833
|
-
const kind = lowerFirstChar(resource.kind);
|
|
15834
|
-
const type = resource.type ? lowerFirstChar(resource.type) : resource.type;
|
|
15835
|
-
if (kind === resource.kind && type === resource.type)
|
|
15836
|
-
continue;
|
|
15837
|
-
resource.kind = kind;
|
|
15838
|
-
resource.type = type;
|
|
15839
|
-
normalized++;
|
|
15840
|
-
}
|
|
15841
|
-
return normalized;
|
|
16370
|
+
function artifactFailure(options, error) {
|
|
16371
|
+
logger.warn(`Solution artifact-resource generation failed for project "${options.projectName}": ${error.message}. ` + "The project was created and registered, but 'uip solution pack' may omit its resource. " + "Re-run init with --force to retry, or run 'uip solution projects add' to finish setup.");
|
|
16372
|
+
return { Created: false, Error: error.message };
|
|
15842
16373
|
}
|
|
15843
|
-
|
|
15844
|
-
|
|
15845
|
-
|
|
15846
|
-
|
|
15847
|
-
|
|
15848
|
-
normalizeResourceCasing(solution);
|
|
15849
|
-
}
|
|
15850
|
-
const [disposeError] = await catchError(builder.disposeAsync());
|
|
15851
|
-
return disposeError ?? null;
|
|
16374
|
+
// ../solution-sdk/src/resources/services/project-template-types.ts
|
|
16375
|
+
var reader;
|
|
16376
|
+
function hasBareTemplateRow(projectType) {
|
|
16377
|
+
reader ??= new EmbeddedProjectConfigurationReader;
|
|
16378
|
+
return reader.getTemplateResourcesByType(projectType, undefined, false).length > 0;
|
|
15852
16379
|
}
|
|
15853
|
-
|
|
15854
|
-
|
|
15855
|
-
const
|
|
15856
|
-
|
|
15857
|
-
|
|
15858
|
-
|
|
15859
|
-
|
|
15860
|
-
|
|
15861
|
-
|
|
15862
|
-
|
|
15863
|
-
|
|
15864
|
-
|
|
15865
|
-
|
|
15866
|
-
|
|
15867
|
-
|
|
15868
|
-
|
|
15869
|
-
|
|
15870
|
-
|
|
15871
|
-
|
|
15872
|
-
|
|
15873
|
-
|
|
15874
|
-
|
|
15875
|
-
}
|
|
15876
|
-
|
|
15877
|
-
|
|
15878
|
-
|
|
15879
|
-
|
|
15880
|
-
|
|
15881
|
-
|
|
15882
|
-
|
|
15883
|
-
|
|
15884
|
-
|
|
15885
|
-
|
|
15886
|
-
|
|
15887
|
-
|
|
15888
|
-
|
|
16380
|
+
function hasAnyTemplateRow(projectType) {
|
|
16381
|
+
reader ??= new EmbeddedProjectConfigurationReader;
|
|
16382
|
+
const wanted = projectType.toLowerCase();
|
|
16383
|
+
return Object.keys(reader.getAll()).some((row) => {
|
|
16384
|
+
const key = row.toLowerCase();
|
|
16385
|
+
return key === wanted || key.startsWith(`${wanted}_`);
|
|
16386
|
+
});
|
|
16387
|
+
}
|
|
16388
|
+
// ../solution-sdk/src/resources/services/remote-resources.ts
|
|
16389
|
+
function getRemoteResourceClients(services) {
|
|
16390
|
+
return {
|
|
16391
|
+
catalog: services.container.resolve(Tokens.IResourceCatalogClientProxy),
|
|
16392
|
+
importer: services.container.resolve(Tokens.IImportResourceHelper)
|
|
16393
|
+
};
|
|
16394
|
+
}
|
|
16395
|
+
async function findCatalogEntityAsync(catalog, query, match) {
|
|
16396
|
+
let pageCursor;
|
|
16397
|
+
do {
|
|
16398
|
+
const page = await catalog.searchFolderEntities({
|
|
16399
|
+
...query,
|
|
16400
|
+
pageSize: 100,
|
|
16401
|
+
pageCursor
|
|
16402
|
+
});
|
|
16403
|
+
const found = (page.items ?? []).find(match);
|
|
16404
|
+
if (found) {
|
|
16405
|
+
return found;
|
|
16406
|
+
}
|
|
16407
|
+
pageCursor = page.nextPage ?? undefined;
|
|
16408
|
+
} while (pageCursor);
|
|
16409
|
+
return;
|
|
16410
|
+
}
|
|
16411
|
+
async function findCatalogEntitiesAsync(catalog, query, match) {
|
|
16412
|
+
const found = [];
|
|
16413
|
+
let pageCursor;
|
|
16414
|
+
do {
|
|
16415
|
+
const page = await catalog.searchFolderEntities({
|
|
16416
|
+
...query,
|
|
16417
|
+
pageSize: 100,
|
|
16418
|
+
pageCursor
|
|
16419
|
+
});
|
|
16420
|
+
found.push(...(page.items ?? []).filter(match));
|
|
16421
|
+
pageCursor = page.nextPage ?? undefined;
|
|
16422
|
+
} while (pageCursor);
|
|
16423
|
+
return found;
|
|
16424
|
+
}
|
|
16425
|
+
function toResourceReference(entity) {
|
|
16426
|
+
return toCatalogReference({
|
|
16427
|
+
kind: entity.entityType ?? "",
|
|
16428
|
+
type: entity.entitySubType ?? undefined,
|
|
16429
|
+
key: entity.entityKey ?? "",
|
|
16430
|
+
name: entity.entityName ?? undefined,
|
|
16431
|
+
folderPath: entity.fullyQualifiedName ?? undefined,
|
|
16432
|
+
folderKey: entity.folderKey ?? undefined
|
|
16433
|
+
});
|
|
16434
|
+
}
|
|
16435
|
+
function toCatalogReference(reference) {
|
|
16436
|
+
const { folderPath, folderKey, ...rest } = reference;
|
|
16437
|
+
return {
|
|
16438
|
+
...rest,
|
|
16439
|
+
folder: folderPath ? { fullyQualifiedName: folderPath, folderKey } : undefined
|
|
16440
|
+
};
|
|
16441
|
+
}
|
|
16442
|
+
async function exportResourceDefinitionAsync(importer, reference) {
|
|
16443
|
+
const payload = await importer.exportResourceAsync(reference);
|
|
16444
|
+
return payload.resourceDefinitions.find((d) => d.key === reference.key);
|
|
16445
|
+
}
|
|
16446
|
+
function readSpecProperty(spec, name) {
|
|
16447
|
+
if (!spec) {
|
|
16448
|
+
return;
|
|
16449
|
+
}
|
|
16450
|
+
if (spec[name] !== undefined) {
|
|
16451
|
+
return spec[name];
|
|
16452
|
+
}
|
|
16453
|
+
const lower = name.toLowerCase();
|
|
16454
|
+
const key = Object.keys(spec).find((k) => k.toLowerCase() === lower);
|
|
16455
|
+
return key === undefined ? undefined : spec[key];
|
|
16456
|
+
}
|
|
16457
|
+
function readSpecEntryPoints(spec) {
|
|
16458
|
+
const entryPoints = parseJsonString(readSpecProperty(spec, "entryPoints"));
|
|
16459
|
+
if (!Array.isArray(entryPoints) || !entryPoints.every((entry) => entry !== null && typeof entry === "object")) {
|
|
16460
|
+
return;
|
|
16461
|
+
}
|
|
16462
|
+
return entryPoints.map((entry) => ({
|
|
16463
|
+
UniqueId: String(readSpecProperty(entry, "uniqueId") ?? ""),
|
|
16464
|
+
Path: String(readSpecProperty(entry, "path") ?? ""),
|
|
16465
|
+
DisplayName: String(readSpecProperty(entry, "displayName") ?? ""),
|
|
16466
|
+
InputArguments: readSpecProperty(entry, "inputArguments"),
|
|
16467
|
+
OutputArguments: readSpecProperty(entry, "outputArguments")
|
|
16468
|
+
}));
|
|
16469
|
+
}
|
|
16470
|
+
function parseJsonString(raw) {
|
|
16471
|
+
if (typeof raw !== "string") {
|
|
16472
|
+
return raw;
|
|
16473
|
+
}
|
|
16474
|
+
const [error, parsed] = catchError(() => JSON.parse(raw));
|
|
16475
|
+
return error ? undefined : parsed;
|
|
16476
|
+
}
|
|
16477
|
+
// ../solution-sdk/src/resources/services/resource-casing.ts
|
|
16478
|
+
var RESOURCE_FILES_FOLDER_NAME2 = "resources";
|
|
16479
|
+
function lowerFirstChar(value) {
|
|
16480
|
+
return value ? value.charAt(0).toLowerCase() + value.slice(1) : value;
|
|
16481
|
+
}
|
|
16482
|
+
function normalizeResourceCasing(solution) {
|
|
16483
|
+
let normalized = 0;
|
|
16484
|
+
for (const resource of solution?.resources ?? []) {
|
|
16485
|
+
const kind = lowerFirstChar(resource.kind);
|
|
16486
|
+
const type = resource.type ? lowerFirstChar(resource.type) : resource.type;
|
|
16487
|
+
if (kind === resource.kind && type === resource.type)
|
|
16488
|
+
continue;
|
|
16489
|
+
resource.kind = kind;
|
|
16490
|
+
resource.type = type;
|
|
16491
|
+
normalized++;
|
|
16492
|
+
}
|
|
16493
|
+
return normalized;
|
|
16494
|
+
}
|
|
16495
|
+
async function saveCasingNormalized(builder) {
|
|
16496
|
+
const [loadError, solution] = await catchError(builder.context.loadAsync());
|
|
16497
|
+
if (loadError) {
|
|
16498
|
+
logger.warn(`Skipping resource casing normalization; failed to load solution: ${loadError.message}`);
|
|
16499
|
+
} else {
|
|
16500
|
+
normalizeResourceCasing(solution);
|
|
16501
|
+
}
|
|
16502
|
+
const [disposeError] = await catchError(builder.disposeAsync());
|
|
16503
|
+
return disposeError ?? null;
|
|
16504
|
+
}
|
|
16505
|
+
async function normalizeResourceFileCasing(solutionDir) {
|
|
16506
|
+
const fs = getFileSystem();
|
|
16507
|
+
const root = fs.path.join(solutionDir, RESOURCE_FILES_FOLDER_NAME2);
|
|
16508
|
+
if (!await fs.exists(root))
|
|
16509
|
+
return [];
|
|
16510
|
+
const byTarget = new Map;
|
|
16511
|
+
for (const [path, target] of await collectResourceFiles(root, [])) {
|
|
16512
|
+
byTarget.set(target, [...byTarget.get(target) ?? [], path]);
|
|
16513
|
+
}
|
|
16514
|
+
const changed = [];
|
|
16515
|
+
for (const [target, paths] of byTarget) {
|
|
16516
|
+
const keep = paths.includes(target) ? target : paths[0];
|
|
16517
|
+
for (const path of paths) {
|
|
16518
|
+
if (path === keep)
|
|
16519
|
+
continue;
|
|
16520
|
+
const [removeError] = await catchError(fs.rm(path));
|
|
16521
|
+
if (removeError) {
|
|
16522
|
+
logger.warn(`Could not remove case-variant resource file ${path}: ${removeError.message}`);
|
|
16523
|
+
continue;
|
|
16524
|
+
}
|
|
16525
|
+
changed.push(path);
|
|
16526
|
+
logger.info(`Removed case-variant resource file ${path}; ${keep} holds the same resource`);
|
|
16527
|
+
}
|
|
16528
|
+
if (keep === target)
|
|
16529
|
+
continue;
|
|
16530
|
+
if (keep.toLowerCase() === target.toLowerCase() && await fs.exists(target)) {
|
|
16531
|
+
continue;
|
|
16532
|
+
}
|
|
16533
|
+
const [makeDirError] = await catchError(fs.mkdir(fs.path.dirname(target)));
|
|
16534
|
+
if (makeDirError) {
|
|
16535
|
+
logger.warn(`Could not create directory for resource file ${target}: ${makeDirError.message}`);
|
|
16536
|
+
continue;
|
|
16537
|
+
}
|
|
16538
|
+
const [renameError] = await catchError(fs.rename(keep, target));
|
|
16539
|
+
if (renameError) {
|
|
16540
|
+
logger.warn(`Could not move resource file ${keep} to ${target}: ${renameError.message}`);
|
|
15889
16541
|
continue;
|
|
15890
16542
|
}
|
|
15891
16543
|
changed.push(target);
|
|
@@ -15900,13 +16552,17 @@ function targetSegments(segments, kind, type) {
|
|
|
15900
16552
|
const kindIndex = type ? target.length - 3 : target.length - 2;
|
|
15901
16553
|
if (kindIndex < 0)
|
|
15902
16554
|
return target;
|
|
15903
|
-
if (target[kindIndex]
|
|
16555
|
+
if (sameIgnoringCase(target[kindIndex], kind)) {
|
|
15904
16556
|
target[kindIndex] = lowerFirstChar(kind);
|
|
15905
|
-
|
|
16557
|
+
}
|
|
16558
|
+
if (type && sameIgnoringCase(target[kindIndex + 1], type)) {
|
|
15906
16559
|
target[kindIndex + 1] = lowerFirstChar(type);
|
|
15907
16560
|
}
|
|
15908
16561
|
return target;
|
|
15909
16562
|
}
|
|
16563
|
+
function sameIgnoringCase(a, b) {
|
|
16564
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
16565
|
+
}
|
|
15910
16566
|
async function collectResourceFiles(root, segments) {
|
|
15911
16567
|
const fs = getFileSystem();
|
|
15912
16568
|
const [readError, names] = await catchError(fs.readdir(fs.path.join(root, ...segments)));
|
|
@@ -15979,13 +16635,168 @@ function parseResourceHeader(content) {
|
|
|
15979
16635
|
return null;
|
|
15980
16636
|
}
|
|
15981
16637
|
}
|
|
15982
|
-
|
|
16638
|
+
// ../solution-sdk/src/resources/services/resource-configurations.ts
|
|
16639
|
+
var ROOT_RESOURCE_TYPES = new Set(["resource", "solutionresource"]);
|
|
16640
|
+
var KEY_NOT_FOUND = /^(?:Solution)?Resource with key '([^']+)' was not found$/i;
|
|
16641
|
+
function isResourceKeyNotFound(error, resourceKey) {
|
|
16642
|
+
if (!(error instanceof Error))
|
|
16643
|
+
return false;
|
|
16644
|
+
const typed = error;
|
|
16645
|
+
const sameKey = (key) => key !== undefined && key.toLowerCase() === resourceKey.toLowerCase();
|
|
16646
|
+
if (typeof typed.resourceType === "string" && typeof typed.resourceKey === "string") {
|
|
16647
|
+
return ROOT_RESOURCE_TYPES.has(typed.resourceType.toLowerCase()) && sameKey(typed.resourceKey);
|
|
16648
|
+
}
|
|
16649
|
+
return sameKey(KEY_NOT_FOUND.exec(error.message.trim())?.[1]);
|
|
16650
|
+
}
|
|
16651
|
+
async function refreshArtefactSpecsAsync(builder, configurations) {
|
|
16652
|
+
const resources = new Map;
|
|
16653
|
+
const [loadError, solution] = await catchError(builder.context.loadAsync());
|
|
16654
|
+
if (loadError || !solution)
|
|
16655
|
+
return resources;
|
|
16656
|
+
for (const configuration of configurations) {
|
|
16657
|
+
const resource = solution.resources.find((r) => r.key === configuration?.key);
|
|
16658
|
+
if (!resource)
|
|
16659
|
+
continue;
|
|
16660
|
+
resources.set(resource.key, resource);
|
|
16661
|
+
if (!resource.projectKey)
|
|
16662
|
+
continue;
|
|
16663
|
+
configuration.spec = await applyEntryPointEnhancementAsync(builder.context, resource, configuration.spec ?? {});
|
|
16664
|
+
}
|
|
16665
|
+
return resources;
|
|
16666
|
+
}
|
|
16667
|
+
async function exportResourceConfigurationsAsync(services, resourceKey, hint = {}) {
|
|
16668
|
+
const { catalog, importer } = getRemoteResourceClients(services);
|
|
16669
|
+
const byKey = (item) => item.entityKey === resourceKey;
|
|
16670
|
+
const entity = (hint.name ? await findCatalogEntityAsync(catalog, { name: hint.name }, byKey) : undefined) ?? await findCatalogEntityAsync(catalog, {}, byKey);
|
|
16671
|
+
if (!entity) {
|
|
16672
|
+
throw new Error(`Resource '${resourceKey}' was not found in the solution or in the resource catalog`);
|
|
16673
|
+
}
|
|
16674
|
+
const payload = await importer.exportResourceAsync(toResourceReference(entity));
|
|
16675
|
+
const definitions = payload.resourceDefinitions ?? [];
|
|
16676
|
+
if (definitions.length === 0) {
|
|
16677
|
+
throw new Error(`FPS export for '${resourceKey}' returned no resource definitions`);
|
|
16678
|
+
}
|
|
16679
|
+
return {
|
|
16680
|
+
resources: definitions.map((d) => ({
|
|
16681
|
+
key: d.key ?? "",
|
|
16682
|
+
spec: d.spec ?? {},
|
|
16683
|
+
locks: d.locks ?? [],
|
|
16684
|
+
apiVersion: d.apiVersion,
|
|
16685
|
+
isOverridable: d.isOverridable,
|
|
16686
|
+
name: d.name ?? "",
|
|
16687
|
+
kind: d.kind ?? "",
|
|
16688
|
+
dependencies: (d.dependencies ?? []).map((dep) => ({
|
|
16689
|
+
kind: dep.kind ?? "",
|
|
16690
|
+
name: dep.name ?? ""
|
|
16691
|
+
}))
|
|
16692
|
+
}))
|
|
16693
|
+
};
|
|
16694
|
+
}
|
|
16695
|
+
async function readLocalAsync(builder, resourceKey, read) {
|
|
16696
|
+
if (!builder)
|
|
16697
|
+
return;
|
|
16698
|
+
const [error, value] = await catchError(read(builder));
|
|
16699
|
+
if (!error)
|
|
16700
|
+
return { builder, value };
|
|
16701
|
+
if (isResourceKeyNotFound(error, resourceKey))
|
|
16702
|
+
return;
|
|
16703
|
+
throw error;
|
|
16704
|
+
}
|
|
16705
|
+
async function readResourceConfigurationsAsync(services, builder, resourceKey, hint = {}) {
|
|
16706
|
+
const local = await readLocalAsync(builder, resourceKey, (b) => b.getAllConfigurationsAsync(resourceKey));
|
|
16707
|
+
if (!local) {
|
|
16708
|
+
return exportResourceConfigurationsAsync(services, resourceKey, hint);
|
|
16709
|
+
}
|
|
16710
|
+
await refreshArtefactSpecsAsync(local.builder, local.value?.resources ?? []);
|
|
16711
|
+
return local.value;
|
|
16712
|
+
}
|
|
16713
|
+
async function readResourceConfigurationAsync(services, builder, resourceKey) {
|
|
16714
|
+
const local = await readLocalAsync(builder, resourceKey, (b) => b.getConfigurationAsync(resourceKey));
|
|
16715
|
+
if (local) {
|
|
16716
|
+
const configuration = local.value;
|
|
16717
|
+
const resources2 = await refreshArtefactSpecsAsync(local.builder, [
|
|
16718
|
+
configuration
|
|
16719
|
+
]);
|
|
16720
|
+
return { configuration, resource: resources2.get(configuration.key) };
|
|
16721
|
+
}
|
|
16722
|
+
const { resources } = await exportResourceConfigurationsAsync(services, resourceKey);
|
|
16723
|
+
const primary = resources.find((r) => r.key === resourceKey) ?? resources[0];
|
|
16724
|
+
const { key, spec, locks, apiVersion, isOverridable } = primary;
|
|
16725
|
+
return { configuration: { key, spec, locks, apiVersion, isOverridable } };
|
|
16726
|
+
}
|
|
16727
|
+
// ../solution-sdk/src/resources/services/resource-lookup.ts
|
|
16728
|
+
async function findResourcesByNameAsync(services, builder, { kind, name, subTypes, folderKey }) {
|
|
16729
|
+
const same = (a, b) => (a ?? "").toLowerCase() === b.toLowerCase();
|
|
16730
|
+
const ofSubType = (type) => subTypes === undefined || subTypes.some((t) => same(type, t));
|
|
16731
|
+
if (builder) {
|
|
16732
|
+
const solution = await builder.context.loadAsync();
|
|
16733
|
+
const local = solution.resources.filter((r) => same(r.kind, kind) && same(r.name, name) && ofSubType(r.type)).map((r) => ({
|
|
16734
|
+
key: r.key,
|
|
16735
|
+
local: true,
|
|
16736
|
+
folderPath: r.folders?.[0]?.fullyQualifiedName
|
|
16737
|
+
}));
|
|
16738
|
+
if (local.length > 0)
|
|
16739
|
+
return local;
|
|
16740
|
+
}
|
|
16741
|
+
const { catalog } = getRemoteResourceClients(services);
|
|
16742
|
+
const rows = await findCatalogEntitiesAsync(catalog, { name, entityTypes: [kind] }, (row) => same(row.entityType, kind) && same(row.entityName, name) && ofSubType(row.entitySubType) && (folderKey === undefined || same(row.folderKey, folderKey)));
|
|
16743
|
+
return rows.map((row) => ({
|
|
16744
|
+
key: row.entityKey ?? "",
|
|
16745
|
+
local: false,
|
|
16746
|
+
folderPath: row.fullyQualifiedName ?? undefined,
|
|
16747
|
+
folderKey: row.folderKey ?? undefined
|
|
16748
|
+
}));
|
|
16749
|
+
}
|
|
16750
|
+
// ../solution-sdk/src/resources/services/resource-name.ts
|
|
16751
|
+
var MAX_RESOURCE_NAME_LENGTH = 256;
|
|
16752
|
+
var UNSAFE_NAME_CHARS = /[\\/:*?"<>|]/;
|
|
16753
|
+
function hasControlChar(value) {
|
|
16754
|
+
for (let i = 0;i < value.length; i++) {
|
|
16755
|
+
if (value.charCodeAt(i) < 32)
|
|
16756
|
+
return true;
|
|
16757
|
+
}
|
|
16758
|
+
return false;
|
|
16759
|
+
}
|
|
16760
|
+
function validateResourceName(name, options = {}) {
|
|
16761
|
+
const optionName = options.optionName ?? "--name";
|
|
16762
|
+
if (name.trim() === "") {
|
|
16763
|
+
return {
|
|
16764
|
+
ok: false,
|
|
16765
|
+
message: `${optionName} must not be blank`,
|
|
16766
|
+
instructions: "Pass a non-empty resource name."
|
|
16767
|
+
};
|
|
16768
|
+
}
|
|
16769
|
+
if (name.length > MAX_RESOURCE_NAME_LENGTH) {
|
|
16770
|
+
return {
|
|
16771
|
+
ok: false,
|
|
16772
|
+
message: `${optionName} exceeds the ${MAX_RESOURCE_NAME_LENGTH}-character limit (got ${name.length})`,
|
|
16773
|
+
instructions: "Shorten the resource name. Orchestrator name limits are kind-specific (queues cap at 50, assets at 256)."
|
|
16774
|
+
};
|
|
16775
|
+
}
|
|
16776
|
+
if (UNSAFE_NAME_CHARS.test(name) || hasControlChar(name)) {
|
|
16777
|
+
return {
|
|
16778
|
+
ok: false,
|
|
16779
|
+
message: `${optionName} contains unsafe characters`,
|
|
16780
|
+
instructions: 'Resource names cannot contain path separators (/, \\), control characters, or any of: : * ? " < > |'
|
|
16781
|
+
};
|
|
16782
|
+
}
|
|
16783
|
+
return { ok: true };
|
|
16784
|
+
}
|
|
16785
|
+
function assertValidResourceName(name, options = {}) {
|
|
16786
|
+
const result = validateResourceName(name, options);
|
|
16787
|
+
if (!result.ok) {
|
|
16788
|
+
throw new Error(`${result.message}. ${result.instructions}`);
|
|
16789
|
+
}
|
|
16790
|
+
}
|
|
15983
16791
|
// ../solution-sdk/src/resources/services/sync-resources-from-bindings.ts
|
|
15984
16792
|
var TYPE_IS_IDENTITY_REPLACE_OPTIONS = new Set([
|
|
15985
16793
|
"SameType",
|
|
15986
16794
|
"SameNameAndType"
|
|
15987
16795
|
]);
|
|
15988
16796
|
var BINDINGS_JSON_FALLBACK_TYPES = new Set(["agent", "function"]);
|
|
16797
|
+
|
|
16798
|
+
class OverwriteRefreshError extends Error {
|
|
16799
|
+
}
|
|
15989
16800
|
function placeholderForType(type) {
|
|
15990
16801
|
switch ((type ?? "string").toLowerCase()) {
|
|
15991
16802
|
case "boolean":
|
|
@@ -16086,13 +16897,15 @@ function resolveBindingResourceIdentity(binding) {
|
|
|
16086
16897
|
};
|
|
16087
16898
|
}
|
|
16088
16899
|
}
|
|
16089
|
-
async function syncResourcesFromBindings(solutionDir, projectPaths, projectSubTypes) {
|
|
16900
|
+
async function syncResourcesFromBindings(solutionDir, projectPaths, projectSubTypes, projectBindings) {
|
|
16090
16901
|
const result = {
|
|
16091
16902
|
created: 0,
|
|
16092
16903
|
imported: 0,
|
|
16093
16904
|
skipped: 0,
|
|
16094
16905
|
pruned: 0,
|
|
16095
|
-
|
|
16906
|
+
projectResourcesCreated: 0,
|
|
16907
|
+
warnings: [],
|
|
16908
|
+
unresolved: []
|
|
16096
16909
|
};
|
|
16097
16910
|
const uipxProjects = await readUipxProjects(solutionDir);
|
|
16098
16911
|
const paths = projectPaths ?? uipxProjects.map((p) => p.path);
|
|
@@ -16106,7 +16919,7 @@ async function syncResourcesFromBindings(solutionDir, projectPaths, projectSubTy
|
|
|
16106
16919
|
return result;
|
|
16107
16920
|
}
|
|
16108
16921
|
const serializer = services.container.resolve(Tokens.IBindingsMetadataSerializer);
|
|
16109
|
-
const { bindings: allBindings, parseErrors } = await collectBindings(solutionDir, paths,
|
|
16922
|
+
const { bindings: allBindings, parseErrors } = await collectBindings(bindingsJsonFallbackDirs, serializer, bindingSources(solutionDir, paths, uipxProjects, projectBindings));
|
|
16110
16923
|
if (parseErrors.length > 0) {
|
|
16111
16924
|
const detail = parseErrors.map((e) => `${e.path}: ${e.reason}`).join("; ");
|
|
16112
16925
|
throw new Error(`Invalid bindings file — ${detail}`);
|
|
@@ -16120,7 +16933,8 @@ async function syncResourcesFromBindings(solutionDir, projectPaths, projectSubTy
|
|
|
16120
16933
|
logger.warn(`Failed to get solution builder: ${builderError.message}`);
|
|
16121
16934
|
return result;
|
|
16122
16935
|
}
|
|
16123
|
-
await reconcileSolutionProjects(builder, solutionDir, projectSubTypes);
|
|
16936
|
+
const reconcileOutcome = await reconcileSolutionProjects(builder, solutionDir, projectSubTypes);
|
|
16937
|
+
result.projectResourcesCreated = reconcileOutcome.resourcesAdded;
|
|
16124
16938
|
const memberProjectKeys = new Set(uipxProjects.map((project) => project.id.toLowerCase()));
|
|
16125
16939
|
await pruneMemberClones(builder, memberProjectKeys, result);
|
|
16126
16940
|
if (allBindings.length === 0) {
|
|
@@ -16135,6 +16949,28 @@ async function syncResourcesFromBindings(solutionDir, projectPaths, projectSubTy
|
|
|
16135
16949
|
const resolveKindMetadata = await buildKindResolver(metadataReader);
|
|
16136
16950
|
const solution = await builder.context.loadAsync();
|
|
16137
16951
|
const knownResourceKeys = new Set((solution.resources ?? []).map((r) => r.key?.toLowerCase()).filter((k) => !!k));
|
|
16952
|
+
let keptUnchecked = 0;
|
|
16953
|
+
const resolvedKeys = new Set;
|
|
16954
|
+
const overwrittenKeys = new Set;
|
|
16955
|
+
const bindingKey = (kind, name) => `${kind}:${name ?? ""}`.toLowerCase();
|
|
16956
|
+
const recordUnchecked = async (error, kind, name, directCloudKey) => {
|
|
16957
|
+
if (error) {
|
|
16958
|
+
const failure = classifyCatalogFailure(error);
|
|
16959
|
+
if (!result.catalogLookupFailure || stopsCatalogLookups(failure) && !stopsCatalogLookups(result.catalogLookupFailure)) {
|
|
16960
|
+
result.catalogLookupFailure = failure;
|
|
16961
|
+
}
|
|
16962
|
+
}
|
|
16963
|
+
const covered = await isUncheckedBindingCovered(builder, knownResourceKeys, kind, name, directCloudKey);
|
|
16964
|
+
if (covered) {
|
|
16965
|
+
keptUnchecked++;
|
|
16966
|
+
result.skipped++;
|
|
16967
|
+
return;
|
|
16968
|
+
}
|
|
16969
|
+
result.unresolved.push({
|
|
16970
|
+
kind,
|
|
16971
|
+
name: directCloudKey ?? name
|
|
16972
|
+
});
|
|
16973
|
+
};
|
|
16138
16974
|
const virtualCandidates = [];
|
|
16139
16975
|
for (const binding of allBindings) {
|
|
16140
16976
|
const identity = resolveBindingResourceIdentity(binding);
|
|
@@ -16164,7 +17000,12 @@ async function syncResourcesFromBindings(solutionDir, projectPaths, projectSubTy
|
|
|
16164
17000
|
logger.info(`Skipping ${kind} "${name}" — the solution provisions this project itself; its member declaration covers the binding`);
|
|
16165
17001
|
continue;
|
|
16166
17002
|
}
|
|
16167
|
-
const
|
|
17003
|
+
const lookupsStopped = !!result.catalogLookupFailure && stopsCatalogLookups(result.catalogLookupFailure);
|
|
17004
|
+
const [lookupError, rcsMatch] = lookupsStopped ? [undefined, undefined] : await catchError(directCloudKey ? findResourceByKeyInRcs(builder, kind, directCloudKey, folderPath, true) : findResourceInRcs(builder, kind, name, folderPath, true));
|
|
17005
|
+
if (lookupsStopped || lookupError) {
|
|
17006
|
+
await recordUnchecked(lookupError, kind, name, directCloudKey);
|
|
17007
|
+
continue;
|
|
17008
|
+
}
|
|
16168
17009
|
const resolvedType = identity.type ?? rcsMatch?.type ?? metadata?.type;
|
|
16169
17010
|
if (!rcsMatch) {
|
|
16170
17011
|
if (isConnection) {
|
|
@@ -16183,30 +17024,48 @@ async function syncResourcesFromBindings(solutionDir, projectPaths, projectSubTy
|
|
|
16183
17024
|
}
|
|
16184
17025
|
if (!isConnection && rcsMatch.key && knownResourceKeys.has(rcsMatch.key.toLowerCase())) {
|
|
16185
17026
|
result.skipped++;
|
|
17027
|
+
resolvedKeys.add(bindingKey(kind, name));
|
|
16186
17028
|
continue;
|
|
16187
17029
|
}
|
|
16188
17030
|
const callType = isConnection ? binding.metadata?.connector : resolvedType;
|
|
16189
|
-
const
|
|
17031
|
+
const wasKnown = !!rcsMatch.key && knownResourceKeys.has(rcsMatch.key.toLowerCase());
|
|
17032
|
+
const reference = {
|
|
16190
17033
|
kind,
|
|
16191
17034
|
type: callType,
|
|
16192
17035
|
key: rcsMatch.key,
|
|
16193
17036
|
name: rcsMatch.name,
|
|
16194
17037
|
folder: rcsMatch.folder
|
|
16195
|
-
}
|
|
17038
|
+
};
|
|
17039
|
+
const [importError] = await catchError(wasKnown ? refreshKnownResource(services, builder, reference, overwrittenKeys) : builder.addOrUpdateResourceToSolutionAsync(reference, true));
|
|
16196
17040
|
if (!importError) {
|
|
17041
|
+
resolvedKeys.add(bindingKey(kind, directCloudKey ?? name));
|
|
17042
|
+
if (wasKnown) {
|
|
17043
|
+
result.skipped++;
|
|
17044
|
+
logger.info(`Refreshed ${kind} "${rcsMatch.name}" (key: ${rcsMatch.key}), already in solution`);
|
|
17045
|
+
continue;
|
|
17046
|
+
}
|
|
16197
17047
|
if (rcsMatch.key) {
|
|
16198
17048
|
knownResourceKeys.add(rcsMatch.key.toLowerCase());
|
|
17049
|
+
overwrittenKeys.add(rcsMatch.key.toLowerCase());
|
|
16199
17050
|
}
|
|
16200
17051
|
result.imported++;
|
|
16201
17052
|
logger.info(`Imported ${kind} "${rcsMatch.name}" (key: ${rcsMatch.key}) into solution`);
|
|
16202
17053
|
continue;
|
|
16203
17054
|
}
|
|
17055
|
+
if (stopsCatalogLookups(classifyCatalogFailure(importError))) {
|
|
17056
|
+
await recordUnchecked(importError, kind, name, directCloudKey);
|
|
17057
|
+
continue;
|
|
17058
|
+
}
|
|
16204
17059
|
if (isConnection) {
|
|
16205
|
-
|
|
16206
|
-
|
|
17060
|
+
const failedStep = importError instanceof OverwriteRefreshError ? "refresh the debug overwrite for" : "import";
|
|
17061
|
+
const warning = `Failed to ${failedStep} connection ${directCloudKey}: ${importError.message}`;
|
|
17062
|
+
result.warnings.push(warning);
|
|
17063
|
+
logger.warn(warning);
|
|
16207
17064
|
continue;
|
|
16208
17065
|
}
|
|
16209
|
-
|
|
17066
|
+
const importWarning = `Failed to import ${kind} "${name}" from Orchestrator: ${importError.message}. Created it locally instead; link it before deploy if it must point at the cloud resource.`;
|
|
17067
|
+
result.warnings.push(importWarning);
|
|
17068
|
+
logger.warn(importWarning);
|
|
16210
17069
|
virtualCandidates.push({
|
|
16211
17070
|
binding,
|
|
16212
17071
|
kind,
|
|
@@ -16225,11 +17084,29 @@ async function syncResourcesFromBindings(solutionDir, projectPaths, projectSubTy
|
|
|
16225
17084
|
return true;
|
|
16226
17085
|
});
|
|
16227
17086
|
for (const candidate of dedupedVirtuals) {
|
|
16228
|
-
const [syncError] = await catchError(createVirtualForBinding(builder, candidate.binding, candidate.kind, candidate.type, candidate.metadata, candidate.name, candidate.folderPath, result));
|
|
17087
|
+
const [syncError, created] = await catchError(createVirtualForBinding(builder, candidate.binding, candidate.kind, candidate.type, candidate.metadata, candidate.name, candidate.folderPath, result));
|
|
16229
17088
|
if (syncError) {
|
|
16230
17089
|
logger.warn(`Failed to create virtual ${candidate.kind}/${candidate.name}: ${syncError.message}`);
|
|
17090
|
+
} else if (created) {
|
|
17091
|
+
resolvedKeys.add(bindingKey(candidate.kind, candidate.name));
|
|
16231
17092
|
}
|
|
16232
17093
|
}
|
|
17094
|
+
if (result.catalogLookupFailure) {
|
|
17095
|
+
const seen = new Set;
|
|
17096
|
+
result.unresolved = result.unresolved.filter((b) => {
|
|
17097
|
+
const key = bindingKey(b.kind, b.name);
|
|
17098
|
+
if (resolvedKeys.has(key)) {
|
|
17099
|
+
keptUnchecked++;
|
|
17100
|
+
result.skipped++;
|
|
17101
|
+
return false;
|
|
17102
|
+
}
|
|
17103
|
+
if (seen.has(key))
|
|
17104
|
+
return false;
|
|
17105
|
+
seen.add(key);
|
|
17106
|
+
return true;
|
|
17107
|
+
});
|
|
17108
|
+
result.warnings.push(catalogLookupWarning(result.catalogLookupFailure, keptUnchecked, result.unresolved));
|
|
17109
|
+
}
|
|
16233
17110
|
const saveError = await saveAndDisposeBuilder(builder, solutionDir);
|
|
16234
17111
|
if (saveError) {
|
|
16235
17112
|
throw new Error(`Failed to save solution changes: ${saveError.message}`);
|
|
@@ -16257,6 +17134,43 @@ async function pruneMemberClones(builder, memberProjectKeys, result) {
|
|
|
16257
17134
|
logger.info(`Removed duplicate ${clone.kind} "${clone.name}" (key: ${clone.key}) — the solution provisions this project itself`);
|
|
16258
17135
|
}
|
|
16259
17136
|
}
|
|
17137
|
+
async function isUncheckedBindingCovered(builder, knownResourceKeys, kind, name, directCloudKey) {
|
|
17138
|
+
if (directCloudKey) {
|
|
17139
|
+
return knownResourceKeys.has(directCloudKey.toLowerCase());
|
|
17140
|
+
}
|
|
17141
|
+
const solution = await builder.context.loadAsync();
|
|
17142
|
+
return !!findMatchingLocalResource(solution.resources ?? [], kind, name, undefined);
|
|
17143
|
+
}
|
|
17144
|
+
async function refreshKnownResource(services, builder, reference, overwrittenKeys) {
|
|
17145
|
+
const solution = await builder.context.loadAsync();
|
|
17146
|
+
const referenceKey = reference.key.toLowerCase();
|
|
17147
|
+
const existing = (solution.resources ?? []).find((resource) => resource.key?.toLowerCase() === referenceKey);
|
|
17148
|
+
const mediator = services.container.resolve(Tokens.IMediator);
|
|
17149
|
+
const response = await mediator.sendCommandAsync(new AddOrUpdateResourceToSolutionCommand(builder.context, reference, true, existing?.key));
|
|
17150
|
+
const sdkWroteOverwrite = response.saved && existing?.key === reference.key;
|
|
17151
|
+
if (!existing || sdkWroteOverwrite || overwrittenKeys.has(referenceKey)) {
|
|
17152
|
+
overwrittenKeys.add(referenceKey);
|
|
17153
|
+
return response;
|
|
17154
|
+
}
|
|
17155
|
+
const [overwriteError] = await catchError(refreshOverwrite(services, builder, reference, existing.key));
|
|
17156
|
+
if (overwriteError)
|
|
17157
|
+
throw new OverwriteRefreshError(overwriteError.message);
|
|
17158
|
+
overwrittenKeys.add(referenceKey);
|
|
17159
|
+
return response;
|
|
17160
|
+
}
|
|
17161
|
+
async function refreshOverwrite(services, builder, reference, solutionResourceKey) {
|
|
17162
|
+
const importer = services.container.resolve(Tokens.IImportResourceHelper);
|
|
17163
|
+
const payload = await importer.exportResourceAsync(reference);
|
|
17164
|
+
const referenceKey = reference.key.toLowerCase();
|
|
17165
|
+
const overwrite = [...payload.overwrites].find(([key]) => key.toLowerCase() === referenceKey)?.[1];
|
|
17166
|
+
if (!overwrite) {
|
|
17167
|
+
throw new Error(`Overwrite not found for resource key '${reference.key}' in exported payload`);
|
|
17168
|
+
}
|
|
17169
|
+
const overwrites = services.container.resolve(Tokens.IResourceOverwritesService);
|
|
17170
|
+
await overwrites.manageSolutionOverwritesAsync(builder.context, [
|
|
17171
|
+
{ solutionResourceKey, overwrite }
|
|
17172
|
+
]);
|
|
17173
|
+
}
|
|
16260
17174
|
async function saveAndDisposeBuilder(builder, solutionDir) {
|
|
16261
17175
|
const context = builder.context;
|
|
16262
17176
|
if (!context?.forceSaveChangesAsync) {
|
|
@@ -16326,11 +17240,11 @@ async function createVirtualForBinding(builder, _binding, kind, type, metadata,
|
|
|
16326
17240
|
if (alreadyVirtualized) {
|
|
16327
17241
|
result.skipped++;
|
|
16328
17242
|
logger.info(`Skipping virtual ${kind} "${name}" — existing resource "${alreadyVirtualized.name}" in solution_folder already covers this binding (requested folder "${folderPath}")`);
|
|
16329
|
-
return;
|
|
17243
|
+
return true;
|
|
16330
17244
|
}
|
|
16331
17245
|
const minted = await mintVirtualResource(builder, kind, type, name, metadata);
|
|
16332
17246
|
if (!minted)
|
|
16333
|
-
return;
|
|
17247
|
+
return false;
|
|
16334
17248
|
result.created++;
|
|
16335
17249
|
logger.info(`Created ${kind} "${name}" in solution`);
|
|
16336
17250
|
if (!virtualizable) {
|
|
@@ -16338,6 +17252,7 @@ async function createVirtualForBinding(builder, _binding, kind, type, metadata,
|
|
|
16338
17252
|
result.warnings.push(warning);
|
|
16339
17253
|
logger.warn(warning);
|
|
16340
17254
|
}
|
|
17255
|
+
return true;
|
|
16341
17256
|
}
|
|
16342
17257
|
async function findResourceByKeyInRcs(builder, kind, expectedKey, folderPath, propagateErrors = false) {
|
|
16343
17258
|
const expectedLower = expectedKey.toLowerCase();
|
|
@@ -16415,8 +17330,8 @@ async function searchRcsResourcesStrict(builder, kind, name) {
|
|
|
16415
17330
|
} while (pageCursor);
|
|
16416
17331
|
return allMatches;
|
|
16417
17332
|
}
|
|
16418
|
-
async function findResourceInRcs(builder, kind, name, folderPath) {
|
|
16419
|
-
const allMatches = await searchRcsResources(builder, kind, name);
|
|
17333
|
+
async function findResourceInRcs(builder, kind, name, folderPath, propagateErrors = false) {
|
|
17334
|
+
const allMatches = propagateErrors ? await searchRcsResourcesStrict(builder, kind, name) : await searchRcsResources(builder, kind, name);
|
|
16420
17335
|
if (allMatches.length === 0) {
|
|
16421
17336
|
return;
|
|
16422
17337
|
}
|
|
@@ -16514,9 +17429,15 @@ async function readUiprojName(uiprojPath) {
|
|
|
16514
17429
|
return;
|
|
16515
17430
|
}
|
|
16516
17431
|
}
|
|
17432
|
+
async function snapshotResourceIdentities(builder) {
|
|
17433
|
+
const [loadError, solution] = await catchError(builder.context.loadAsync());
|
|
17434
|
+
if (loadError || !solution)
|
|
17435
|
+
return;
|
|
17436
|
+
return new Set((solution.resources ?? []).map((resource) => resource.key ? resource.key.toLowerCase() : `${resource.kind}:${resource.name}`.toLowerCase()));
|
|
17437
|
+
}
|
|
16517
17438
|
async function reconcileSolutionProjects(builder, solutionDir, projectSubTypes) {
|
|
16518
17439
|
const fs = getFileSystem();
|
|
16519
|
-
const outcome = { projectsCount: 0 };
|
|
17440
|
+
const outcome = { projectsCount: 0, resourcesAdded: 0 };
|
|
16520
17441
|
const uipxProjects = await readUipxProjects(solutionDir);
|
|
16521
17442
|
const projectDetails = uipxProjects.flatMap((project) => {
|
|
16522
17443
|
const reconcileType = mapUipxTypeToReconcileType(project.type);
|
|
@@ -16534,6 +17455,7 @@ async function reconcileSolutionProjects(builder, solutionDir, projectSubTypes)
|
|
|
16534
17455
|
if (projectDetails.length === 0)
|
|
16535
17456
|
return outcome;
|
|
16536
17457
|
const solutionName = fs.path.basename(solutionDir);
|
|
17458
|
+
const before = await snapshotResourceIdentities(builder);
|
|
16537
17459
|
const [reconcileError] = await catchError(builder.reconcileProjectsAsync({
|
|
16538
17460
|
solutionName,
|
|
16539
17461
|
projects: projectDetails
|
|
@@ -16543,6 +17465,10 @@ async function reconcileSolutionProjects(builder, solutionDir, projectSubTypes)
|
|
|
16543
17465
|
return outcome;
|
|
16544
17466
|
}
|
|
16545
17467
|
outcome.projectsCount = projectDetails.length;
|
|
17468
|
+
const after = before && await snapshotResourceIdentities(builder);
|
|
17469
|
+
if (before && after) {
|
|
17470
|
+
outcome.resourcesAdded = [...after].filter((identity) => !before.has(identity)).length;
|
|
17471
|
+
}
|
|
16546
17472
|
logger.info(`Reconciled ${projectDetails.length} .uipx project(s) in solution ${solutionName}`);
|
|
16547
17473
|
return outcome;
|
|
16548
17474
|
}
|
|
@@ -16584,12 +17510,48 @@ function findBindingsSchemaError(content) {
|
|
|
16584
17510
|
}
|
|
16585
17511
|
return null;
|
|
16586
17512
|
}
|
|
16587
|
-
|
|
17513
|
+
function bindingSources(solutionDir, projectPaths, projects, projectBindings) {
|
|
17514
|
+
const sources = [{ directory: solutionDir }];
|
|
17515
|
+
const fileDirectories = new Set([solutionDir]);
|
|
17516
|
+
const contributedIds = new Set;
|
|
17517
|
+
for (const directory of new Set(projectPaths)) {
|
|
17518
|
+
const owners = projects.filter((project) => project.path === directory);
|
|
17519
|
+
for (const project of owners) {
|
|
17520
|
+
const bindings = projectBindings?.get(project.id);
|
|
17521
|
+
if (bindings === undefined || contributedIds.has(project.id))
|
|
17522
|
+
continue;
|
|
17523
|
+
contributedIds.add(project.id);
|
|
17524
|
+
sources.push({
|
|
17525
|
+
directory,
|
|
17526
|
+
contribution: { projectId: project.id, bindings }
|
|
17527
|
+
});
|
|
17528
|
+
}
|
|
17529
|
+
const needsFile = owners.length === 0 || owners.some((project) => !projectBindings?.has(project.id));
|
|
17530
|
+
if (needsFile && !fileDirectories.has(directory)) {
|
|
17531
|
+
fileDirectories.add(directory);
|
|
17532
|
+
sources.push({ directory });
|
|
17533
|
+
}
|
|
17534
|
+
}
|
|
17535
|
+
return sources;
|
|
17536
|
+
}
|
|
17537
|
+
async function collectBindings(bindingsJsonFallbackDirs, serializer, sources) {
|
|
16588
17538
|
const fs = getFileSystem();
|
|
16589
17539
|
const allBindings = [];
|
|
16590
17540
|
const parseErrors = [];
|
|
16591
|
-
const
|
|
16592
|
-
|
|
17541
|
+
for (const { directory: dir, contribution } of sources) {
|
|
17542
|
+
if (contribution) {
|
|
17543
|
+
if (contribution.bindings.length === 0)
|
|
17544
|
+
continue;
|
|
17545
|
+
const parsed2 = deserializeBindings(JSON.stringify({
|
|
17546
|
+
version: "2.0",
|
|
17547
|
+
resources: contribution.bindings
|
|
17548
|
+
}), `${dir} (bindings contributed by the project's tool; project ${contribution.projectId})`, serializer);
|
|
17549
|
+
if ("error" in parsed2)
|
|
17550
|
+
parseErrors.push(parsed2.error);
|
|
17551
|
+
else
|
|
17552
|
+
allBindings.push(...parsed2.resources);
|
|
17553
|
+
continue;
|
|
17554
|
+
}
|
|
16593
17555
|
const v2Path = fs.path.join(dir, "bindings_v2.json");
|
|
16594
17556
|
let bindingsPath = v2Path;
|
|
16595
17557
|
let exists = await fs.exists(v2Path);
|
|
@@ -16608,37 +17570,44 @@ async function collectBindings(solutionDir, projectPaths, bindingsJsonFallbackDi
|
|
|
16608
17570
|
});
|
|
16609
17571
|
continue;
|
|
16610
17572
|
}
|
|
16611
|
-
const
|
|
16612
|
-
|
|
16613
|
-
|
|
16614
|
-
|
|
16615
|
-
|
|
16616
|
-
|
|
16617
|
-
|
|
16618
|
-
|
|
16619
|
-
|
|
16620
|
-
|
|
17573
|
+
const parsed = deserializeBindings(new TextDecoder().decode(content), bindingsPath, serializer);
|
|
17574
|
+
if ("error" in parsed)
|
|
17575
|
+
parseErrors.push(parsed.error);
|
|
17576
|
+
else
|
|
17577
|
+
allBindings.push(...parsed.resources);
|
|
17578
|
+
}
|
|
17579
|
+
return { bindings: allBindings, parseErrors };
|
|
17580
|
+
}
|
|
17581
|
+
function deserializeBindings(decoded, source, serializer) {
|
|
17582
|
+
let success;
|
|
17583
|
+
let metadata;
|
|
17584
|
+
try {
|
|
17585
|
+
const result = serializer.tryDeserialize(decoded);
|
|
17586
|
+
success = result.success;
|
|
17587
|
+
metadata = result.metadata;
|
|
17588
|
+
} catch (err) {
|
|
17589
|
+
return {
|
|
17590
|
+
error: {
|
|
17591
|
+
path: source,
|
|
16621
17592
|
reason: findBindingsSchemaError(decoded) ?? `does not match the expected v2 bindings schema — check each resource's 'kind' and required fields${err instanceof Error ? ` (${err.message})` : ""}`
|
|
16622
|
-
}
|
|
16623
|
-
|
|
16624
|
-
|
|
16625
|
-
|
|
16626
|
-
|
|
16627
|
-
|
|
17593
|
+
}
|
|
17594
|
+
};
|
|
17595
|
+
}
|
|
17596
|
+
if (!success) {
|
|
17597
|
+
return {
|
|
17598
|
+
error: {
|
|
17599
|
+
path: source,
|
|
16628
17600
|
reason: findBindingsSchemaError(decoded) ?? "does not match the expected v2 bindings schema (the serializer rejected it)"
|
|
16629
|
-
}
|
|
16630
|
-
|
|
16631
|
-
}
|
|
16632
|
-
if (!metadata?.resources?.length) {
|
|
16633
|
-
continue;
|
|
16634
|
-
}
|
|
16635
|
-
allBindings.push(...metadata.resources);
|
|
17601
|
+
}
|
|
17602
|
+
};
|
|
16636
17603
|
}
|
|
16637
|
-
return {
|
|
17604
|
+
return { resources: metadata?.resources ?? [] };
|
|
16638
17605
|
}
|
|
16639
|
-
async function syncAndLog(solutionDir, projectSubTypes) {
|
|
16640
|
-
const [syncError, syncResult] = await catchError(syncResourcesFromBindings(solutionDir, undefined, projectSubTypes));
|
|
17606
|
+
async function syncAndLog(solutionDir, projectSubTypes, options) {
|
|
17607
|
+
const [syncError, syncResult] = await catchError(syncResourcesFromBindings(solutionDir, undefined, projectSubTypes, options?.projectBindings));
|
|
16641
17608
|
if (syncError) {
|
|
17609
|
+
if (options?.failOnUnresolved)
|
|
17610
|
+
throw syncError;
|
|
16642
17611
|
logger.warn(`Resource sync failed: ${syncError.message}. Continuing.`);
|
|
16643
17612
|
return;
|
|
16644
17613
|
}
|
|
@@ -16648,6 +17617,9 @@ async function syncAndLog(solutionDir, projectSubTypes) {
|
|
|
16648
17617
|
for (const warning of syncResult.warnings) {
|
|
16649
17618
|
logger.warn(warning);
|
|
16650
17619
|
}
|
|
17620
|
+
if (options?.failOnUnresolved && syncResult.catalogLookupFailure && syncResult.unresolved.length > 0) {
|
|
17621
|
+
throw new CatalogLookupError(syncResult.catalogLookupFailure, syncResult.unresolved);
|
|
17622
|
+
}
|
|
16651
17623
|
}
|
|
16652
17624
|
|
|
16653
17625
|
// ../solution-sdk/src/resources/services/resource-refresh-service.ts
|
|
@@ -16684,7 +17656,11 @@ async function resourceRefreshAsync(solutionPath = ".", options = {}) {
|
|
|
16684
17656
|
const resolverWarnings = subTypesError ? [
|
|
16685
17657
|
`Could not resolve project subTypes: ${subTypesError.message}. Project artefacts for types that need a variant were left as they are; fix the tool install and re-run 'uip solution resources refresh'.`
|
|
16686
17658
|
] : [];
|
|
16687
|
-
const [
|
|
17659
|
+
const [bindingsError, projectBindings] = await catchError(options.resolveProjectBindingsAsync?.() ?? Promise.resolve(new Map));
|
|
17660
|
+
if (bindingsError) {
|
|
17661
|
+
resolverWarnings.push(`Could not read the bindings projects contribute: ${bindingsError.message}. Those projects were synced from any bindings_v2.json on disk instead; fix the tool install and re-run 'uip solution resources refresh'.`);
|
|
17662
|
+
}
|
|
17663
|
+
const [syncError, result] = await catchError(syncResourcesFromBindings(resolvedPath, undefined, projectSubTypes ?? new Map, projectBindings ?? new Map));
|
|
16688
17664
|
if (syncError) {
|
|
16689
17665
|
return {
|
|
16690
17666
|
ok: false,
|
|
@@ -16698,99 +17674,10 @@ async function resourceRefreshAsync(solutionPath = ".", options = {}) {
|
|
|
16698
17674
|
imported: result.imported,
|
|
16699
17675
|
skipped: result.skipped,
|
|
16700
17676
|
pruned: result.pruned,
|
|
17677
|
+
projectResourcesCreated: result.projectResourcesCreated,
|
|
16701
17678
|
warnings: [...resolverWarnings, ...result.warnings]
|
|
16702
17679
|
};
|
|
16703
17680
|
}
|
|
16704
|
-
// ../solution-sdk/src/resources/services/remote-resources.ts
|
|
16705
|
-
function getRemoteResourceClients(services) {
|
|
16706
|
-
return {
|
|
16707
|
-
catalog: services.container.resolve(Tokens.IResourceCatalogClientProxy),
|
|
16708
|
-
importer: services.container.resolve(Tokens.IImportResourceHelper)
|
|
16709
|
-
};
|
|
16710
|
-
}
|
|
16711
|
-
async function findCatalogEntityAsync(catalog, query, match) {
|
|
16712
|
-
let pageCursor;
|
|
16713
|
-
do {
|
|
16714
|
-
const page = await catalog.searchFolderEntities({
|
|
16715
|
-
...query,
|
|
16716
|
-
pageSize: 100,
|
|
16717
|
-
pageCursor
|
|
16718
|
-
});
|
|
16719
|
-
const found = (page.items ?? []).find(match);
|
|
16720
|
-
if (found) {
|
|
16721
|
-
return found;
|
|
16722
|
-
}
|
|
16723
|
-
pageCursor = page.nextPage ?? undefined;
|
|
16724
|
-
} while (pageCursor);
|
|
16725
|
-
return;
|
|
16726
|
-
}
|
|
16727
|
-
function toResourceReference(entity) {
|
|
16728
|
-
return toCatalogReference({
|
|
16729
|
-
kind: entity.entityType ?? "",
|
|
16730
|
-
type: entity.entitySubType ?? undefined,
|
|
16731
|
-
key: entity.entityKey ?? "",
|
|
16732
|
-
name: entity.entityName ?? undefined,
|
|
16733
|
-
folderPath: entity.fullyQualifiedName ?? undefined,
|
|
16734
|
-
folderKey: entity.folderKey ?? undefined
|
|
16735
|
-
});
|
|
16736
|
-
}
|
|
16737
|
-
function toCatalogReference(reference) {
|
|
16738
|
-
const { folderPath, folderKey, ...rest } = reference;
|
|
16739
|
-
return {
|
|
16740
|
-
...rest,
|
|
16741
|
-
folder: folderPath ? { fullyQualifiedName: folderPath, folderKey } : undefined
|
|
16742
|
-
};
|
|
16743
|
-
}
|
|
16744
|
-
async function exportResourceDefinitionAsync(importer, reference) {
|
|
16745
|
-
const payload = await importer.exportResourceAsync(reference);
|
|
16746
|
-
return payload.resourceDefinitions.find((d) => d.key === reference.key);
|
|
16747
|
-
}
|
|
16748
|
-
function readSpecProperty(spec, name) {
|
|
16749
|
-
if (!spec) {
|
|
16750
|
-
return;
|
|
16751
|
-
}
|
|
16752
|
-
if (spec[name] !== undefined) {
|
|
16753
|
-
return spec[name];
|
|
16754
|
-
}
|
|
16755
|
-
const lower = name.toLowerCase();
|
|
16756
|
-
const key = Object.keys(spec).find((k) => k.toLowerCase() === lower);
|
|
16757
|
-
return key === undefined ? undefined : spec[key];
|
|
16758
|
-
}
|
|
16759
|
-
// ../solution-sdk/src/resources/services/resource-name.ts
|
|
16760
|
-
var MAX_RESOURCE_NAME_LENGTH = 256;
|
|
16761
|
-
var UNSAFE_NAME_CHARS = /[\\/:*?"<>|]/;
|
|
16762
|
-
function hasControlChar(value) {
|
|
16763
|
-
for (let i = 0;i < value.length; i++) {
|
|
16764
|
-
if (value.charCodeAt(i) < 32)
|
|
16765
|
-
return true;
|
|
16766
|
-
}
|
|
16767
|
-
return false;
|
|
16768
|
-
}
|
|
16769
|
-
function validateResourceName(name, options = {}) {
|
|
16770
|
-
const optionName = options.optionName ?? "--name";
|
|
16771
|
-
if (name.trim() === "") {
|
|
16772
|
-
return {
|
|
16773
|
-
ok: false,
|
|
16774
|
-
message: `${optionName} must not be blank`,
|
|
16775
|
-
instructions: "Pass a non-empty resource name."
|
|
16776
|
-
};
|
|
16777
|
-
}
|
|
16778
|
-
if (name.length > MAX_RESOURCE_NAME_LENGTH) {
|
|
16779
|
-
return {
|
|
16780
|
-
ok: false,
|
|
16781
|
-
message: `${optionName} exceeds the ${MAX_RESOURCE_NAME_LENGTH}-character limit (got ${name.length})`,
|
|
16782
|
-
instructions: "Shorten the resource name. Orchestrator name limits are kind-specific (queues cap at 50, assets at 256)."
|
|
16783
|
-
};
|
|
16784
|
-
}
|
|
16785
|
-
if (UNSAFE_NAME_CHARS.test(name) || hasControlChar(name)) {
|
|
16786
|
-
return {
|
|
16787
|
-
ok: false,
|
|
16788
|
-
message: `${optionName} contains unsafe characters`,
|
|
16789
|
-
instructions: 'Resource names cannot contain path separators (/, \\), control characters, or any of: : * ? " < > |'
|
|
16790
|
-
};
|
|
16791
|
-
}
|
|
16792
|
-
return { ok: true };
|
|
16793
|
-
}
|
|
16794
17681
|
// ../solution-sdk/src/resources/services/resource-remote-sync.ts
|
|
16795
17682
|
var NOT_FOUND_PATTERN = /not found/i;
|
|
16796
17683
|
async function resourceRemoteSyncAsync(solutionPath = ".", resourceKey, options = {}) {
|
|
@@ -16968,6 +17855,6 @@ function sortKeysDeep(value) {
|
|
|
16968
17855
|
}
|
|
16969
17856
|
return sorted;
|
|
16970
17857
|
}
|
|
16971
|
-
export { Tokens, models_BindingsResourceType, getResourceName, getFolderPathProperty, getResourceKindForBinding, assertSolutionManifest, computeEntryPointSpec, applyEntryPointEnhancementAsync, createResourceBuilderServices, findProjectFile, isProjectManifestError, isMissingProjectTypeError, readProjectFile, readSolutionFile, resolveSolutionFilePath, writeSolutionFile, restoreSolutionFile, createProjectInSolutionBuilder, normalizeFolderPath, findMatchingLocalResource, addProjectArtifactsToSolutionAsync, hasBareTemplateRow, hasAnyTemplateRow, getRemoteResourceClients, findCatalogEntityAsync, toResourceReference, exportResourceDefinitionAsync, readSpecProperty, saveCasingNormalized, normalizeResourceFileCasing, validateResourceName, formatResourceSubtypes, sameResourceSubtype, syncResourcesFromBindings, findResourceByKeyInRcs, searchRcsResources, findResourceInRcs, createResourceKindMetadataResolver, resolveResourceKindMetadata, createLocalResource, readUipxProjects, syncAndLog, resourceRefreshAsync, resourceRemoteSyncAsync };
|
|
17858
|
+
export { Tokens, models_BindingsResourceType, getResourceName, getFolderPathProperty, getResourceKindForBinding, assertSolutionManifest, readSolutionManifest, resolveProjectDirFromManifest, EntryPointSpecEnhancer, computeEntryPointSpec, applyEntryPointEnhancementAsync, createResourceBuilderServices, findProjectFile, ProjectManifestError, isProjectManifestError, isMissingProjectTypeError, readProjectFile, readSolutionFile, resolveSolutionFilePath, writeSolutionFile, restoreSolutionFile, createProjectInSolutionBuilder, CatalogLookupError, normalizeFolderPath, findMatchingLocalResource, findMatchingMemberResource, findMemberCloneResources, addProjectArtifactsToSolutionAsync, hasBareTemplateRow, hasAnyTemplateRow, getRemoteResourceClients, findCatalogEntityAsync, findCatalogEntitiesAsync, toResourceReference, toCatalogReference, exportResourceDefinitionAsync, readSpecProperty, readSpecEntryPoints, normalizeResourceCasing, saveCasingNormalized, normalizeResourceFileCasing, isResourceKeyNotFound, refreshArtefactSpecsAsync, exportResourceConfigurationsAsync, readResourceConfigurationsAsync, readResourceConfigurationAsync, findResourcesByNameAsync, MAX_RESOURCE_NAME_LENGTH, validateResourceName, assertValidResourceName, formatResourceSubtypes, sameResourceSubtype, syncResourcesFromBindings, uniqueNameForResource, findResourceByKeyInRcs, searchRcsResources, searchRcsResourcesStrict, findResourceInRcs, createResourceKindMetadataResolver, resolveResourceKindMetadata, createLocalResource, readUipxProjects, syncAndLog, resourceRefreshAsync, resourceRemoteSyncAsync };
|
|
16972
17859
|
|
|
16973
|
-
//# debugId=
|
|
17860
|
+
//# debugId=D96A40C680DB2DF664756E2164756E21
|