@uipath/solution-tool 1.204.0 → 1.205.0-preview.258

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.
Files changed (122) hide show
  1. package/README.md +3 -3
  2. package/dist/THIRD-PARTY-NOTICES.md +2 -2
  3. package/dist/archive-extract-x35t9ktj.js +120 -0
  4. package/dist/cleanup-command-service-rc5e42hk.js +149 -0
  5. package/dist/deploy-delete-service-qdbtj3qd.js +25 -0
  6. package/dist/deploy-run-service-m9mg34n5.js +25 -0
  7. package/dist/{deploy-status-service-3req7hje.js → deploy-status-service-kz3nk43q.js} +41 -33
  8. package/dist/deploy.js +23 -17
  9. package/dist/{deployment-search-3mah6f0a.js → deployment-search-jvwyaqek.js} +16 -10
  10. package/dist/{embedded-file-reader-rf2g1kfj.js → embedded-file-reader-z4becc02.js} +2 -2
  11. package/dist/{feed-resolver-bxj9xkgr.js → feed-resolver-3xb7ayx4.js} +10 -9
  12. package/dist/feed-scope-failure-qps17dv3.js +19 -0
  13. package/dist/{feed-session-sxga31vn.js → feed-session-7xqphdrg.js} +3 -3
  14. package/dist/governance-options-amc1v319.js +32 -0
  15. package/dist/index-6wak6hg6.js +667 -0
  16. package/dist/index-prm27bvm.js +138 -0
  17. package/dist/index.js +27 -29
  18. package/dist/init.js +11 -10
  19. package/dist/{list-s5pkkefj.js → list-7jy11vb4.js} +10 -8
  20. package/dist/models/pack-command-types.d.ts +15 -0
  21. package/dist/node-p1scvsw8.js +86 -0
  22. package/dist/pack-command-service-8etwpgm9.js +35 -0
  23. package/dist/pack-project-hjvbvhgx.js +97 -0
  24. package/dist/pack.js +24 -14
  25. package/dist/package-id-c18sx4mz.js +13 -0
  26. package/dist/packager-tool-09fwj7e8.js +171 -0
  27. package/dist/{packager-tool-nfmygm61.js → packager-tool-361zx07h.js} +74 -23
  28. package/dist/packager-tool-37jmavap.js +42 -0
  29. package/dist/{packager-tool-px1j9kre.js → packager-tool-3bwnkjdq.js} +2 -2
  30. package/dist/{packager-tool-gb35b4qs.js → packager-tool-43ahv5mn.js} +192 -96
  31. package/dist/packager-tool-47wzvqw0.js +86 -0
  32. package/dist/{packager-tool-gfhdpkp3.js → packager-tool-4bq36bf5.js} +25 -4
  33. package/dist/packager-tool-4jekgh4s.js +517 -0
  34. package/dist/{packager-tool-zns6n8zp.js → packager-tool-5ddtwzva.js} +1 -1
  35. package/dist/packager-tool-5edvzz80.js +543 -0
  36. package/dist/packager-tool-5y09xhx5.js +1333 -0
  37. package/dist/packager-tool-60af1hw2.js +26 -0
  38. package/dist/{packager-tool-f5gq77m5.js → packager-tool-61pwb567.js} +2 -2
  39. package/dist/{packager-tool-kdwsw2x7.js → packager-tool-69jmxeh0.js} +196 -1519
  40. package/dist/{packager-tool-yrz4mwfn.js → packager-tool-6kz1pv9f.js} +1373 -486
  41. package/dist/{packager-tool-stsnbewn.js → packager-tool-6x35kvbx.js} +17 -8
  42. package/dist/packager-tool-6ybywxxn.js +1809 -0
  43. package/dist/{packager-tool-wfkv9v8b.js → packager-tool-85pen8t9.js} +2196 -3455
  44. package/dist/packager-tool-8ptbmmmp.js +2132 -0
  45. package/dist/{packager-tool-9ex75r32.js → packager-tool-8q4tvv8d.js} +6 -4
  46. package/dist/packager-tool-8rg0s4w9.js +109 -0
  47. package/dist/{packager-tool-7gr2nphb.js → packager-tool-97rcgwvv.js} +224 -6
  48. package/dist/packager-tool-act5r386.js +239 -0
  49. package/dist/packager-tool-amhyxzz6.js +125 -0
  50. package/dist/{packager-tool-j3520gxw.js → packager-tool-an5bpbwe.js} +3556 -1646
  51. package/dist/{packager-tool-e2rtr49j.js → packager-tool-b2nhscfk.js} +15 -31
  52. package/dist/packager-tool-bqw7edje.js +188 -0
  53. package/dist/{packager-tool-4gs8y6pr.js → packager-tool-dwxswyv4.js} +149 -53
  54. package/dist/{packager-tool-fehvgd51.js → packager-tool-e7vczxa8.js} +357 -1370
  55. package/dist/{packager-tool-v8bkkqdb.js → packager-tool-gmwva4yv.js} +5 -5
  56. package/dist/packager-tool-gs3f3tf7.js +103 -0
  57. package/dist/packager-tool-kkfb5kgn.js +182 -0
  58. package/dist/{packager-tool-ds45xby2.js → packager-tool-m74zqwmh.js} +104 -14
  59. package/dist/{packager-tool-fv7xyzg2.js → packager-tool-pbmqk39a.js} +126 -128
  60. package/dist/packager-tool-q5j6k3fy.js +1510 -0
  61. package/dist/packager-tool-qk21gk5m.js +445 -0
  62. package/dist/{packager-tool-bqre28m1.js → packager-tool-qw47eyw7.js} +1 -1
  63. package/dist/packager-tool-resolver-994z3jkj.js +23 -0
  64. package/dist/{packager-tool-gab9y5ct.js → packager-tool-t8gh825q.js} +37 -13
  65. package/dist/{packager-tool-kswwd3hp.js → packager-tool-tx834spe.js} +2376 -4077
  66. package/dist/{packager-tool-zf3929se.js → packager-tool-v1f5e8t9.js} +11 -1
  67. package/dist/{packager-tool-g8kjzrg7.js → packager-tool-wknfv4p5.js} +13 -11
  68. package/dist/{packager-tool-1rzssy0x.js → packager-tool-wy4fx250.js} +19 -18
  69. package/dist/packager-tool-y2k2f6yg.js +68 -0
  70. package/dist/packager-tool.js +2 -2
  71. package/dist/prepare-solution-resources-m1djfj62.js +29 -0
  72. package/dist/project-command-utils-t6pmpvq3.js +90 -0
  73. package/dist/project-contributions-79ndycrx.js +32 -0
  74. package/dist/publish.js +13 -12
  75. package/dist/resource-mutations-c3krjqze.js +36 -0
  76. package/dist/resource-version-lookup-0e3e2j58.js +99 -0
  77. package/dist/resource.js +10 -9
  78. package/dist/restore-command-service-eyx911f2.js +155 -0
  79. package/dist/run-service-p1vzzqe3.js +428 -0
  80. package/dist/services/deploy-activate-service.d.ts +10 -5
  81. package/dist/services/deploy-delete-service.d.ts +8 -4
  82. package/dist/services/deploy-run-service.d.ts +34 -1
  83. package/dist/services/deploy-uninstall-service.d.ts +21 -3
  84. package/dist/services/deploy-upgrade-service.d.ts +35 -1
  85. package/dist/services/deployment-feed-locator.d.ts +15 -4
  86. package/dist/services/deployment-search-client.d.ts +5 -53
  87. package/dist/services/deployment-search.d.ts +58 -3
  88. package/dist/services/deployment-state.d.ts +84 -24
  89. package/dist/services/deployment-validation.d.ts +5 -0
  90. package/dist/services/emit-runtime-dependencies.d.ts +2 -1
  91. package/dist/services/feed-resolver.d.ts +6 -4
  92. package/dist/services/feed-selection.d.ts +47 -0
  93. package/dist/services/http-status.d.ts +2 -7
  94. package/dist/services/pack-command-service.d.ts +9 -0
  95. package/dist/services/pack-project-selection.d.ts +51 -0
  96. package/dist/services/pack-project.d.ts +26 -0
  97. package/dist/services/pack-service.d.ts +7 -3
  98. package/dist/services/package-search-client.d.ts +7 -0
  99. package/dist/services/packager-tool-resolver.d.ts +7 -1
  100. package/dist/services/packages-list-service.d.ts +1 -1
  101. package/dist/services/permanent-deletion-consent.d.ts +110 -0
  102. package/dist/services/pipeline-deploy-poll.d.ts +7 -0
  103. package/dist/services/prepare-solution-resources.d.ts +12 -2
  104. package/dist/services/project-contributions.d.ts +5 -4
  105. package/dist/services/search-failure.d.ts +9 -13
  106. package/dist/services/solution-packager-helpers.d.ts +5 -2
  107. package/dist/services/solution-path.d.ts +1 -1
  108. package/dist/services/uninstall-consent.d.ts +82 -0
  109. package/dist/ship-telemetry-myes7ak5.js +21 -0
  110. package/dist/solution-auth-aead70sw.js +11 -0
  111. package/dist/templates/AGENTS.md +125 -12
  112. package/dist/tool.js +27 -29
  113. package/dist/utils/deployment-errors.d.ts +9 -1
  114. package/package.json +2 -2
  115. package/dist/deploy-delete-service-1zydyyhj.js +0 -22
  116. package/dist/feed-scope-failure-2kndwpf6.js +0 -18
  117. package/dist/packager-tool-f2e9rzxk.js +0 -104
  118. package/dist/packager-tool-resolver-6zwhec62.js +0 -20
  119. package/dist/packager-tool-zdfn32bc.js +0 -4465
  120. package/dist/prepare-solution-resources-jpb5as2s.js +0 -25
  121. package/dist/project-contributions-6wpff2hm.js +0 -30
  122. 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-f5gq77m5.js";
12
+ } from "./packager-tool-61pwb567.js";
13
13
  import {
14
14
  UsersApi,
15
15
  createApiClient
16
- } from "./packager-tool-zdfn32bc.js";
16
+ } from "./packager-tool-8ptbmmmp.js";
17
17
  import {
18
18
  findSolutionFileUpward,
19
19
  normalizeProjectType,
20
20
  toPortableRelativePath
21
- } from "./packager-tool-f2e9rzxk.js";
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-kswwd3hp.js";
28
+ } from "./packager-tool-tx834spe.js";
28
29
  import {
29
- getLoginStatusAsync
30
- } from "./packager-tool-gb35b4qs.js";
30
+ catchError,
31
+ extractErrorMessage
32
+ } from "./packager-tool-6ybywxxn.js";
31
33
  import {
32
34
  getFileSystem
33
- } from "./packager-tool-zf3929se.js";
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 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();
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
- context.items[SolutionContextKeys.PARSE_ERRORS_ITEM_KEY] = parseErrors;
3563
- if (!context.isReadOnly) {
3564
- hashes = this.getHashes(context);
3565
- paths = this.getPaths(context);
3566
- tree = this.getTree(context);
3567
- hashes.clear();
3568
- paths.clear();
3569
- tree.length = 0;
3570
- }
3571
- const resourceFiles = await fileStorageProvider.getFilesAsync(SolutionConstants.RESOURCE_FILES_FOLDER_NAME, "*.json", cancellationToken);
3572
- for (const resourceFile of resourceFiles)
3573
- try {
3574
- const stream = await fileStorageProvider.openFileAsync(resourceFile, cancellationToken);
3575
- const content = await this.streamToString(stream);
3576
- const wrapper = this.caseInsensitiveSerializer.deserialize(content);
3577
- if (wrapper?.resource) {
3578
- const rd = this.fromSerializable(wrapper.resource, solutionFiles);
3579
- const relPath = resourceFile.replace(/\\/g, "/");
3580
- const segments = relPath.split("/").filter((s) => s.length > 0);
3581
- const firstSegment = segments[0];
3582
- if (segments.length >= 3 && firstSegment && firstSegment.toLowerCase() === SolutionConstants.RESOURCE_FILES_FOLDER_NAME.toLowerCase()) {
3583
- const hasType = !!rd.type;
3584
- const folderSegmentCount = hasType ? segments.length - 3 : segments.length - 2;
3585
- if (folderSegmentCount < 1)
3586
- rd.folders = [
3587
- {
3588
- fullyQualifiedName: SolutionConstants.ROOT_FOLDER_NAME
3589
- }
3590
- ];
3591
- else {
3592
- const segmentsToUse = segments.slice(1, folderSegmentCount);
3593
- const folderFqn = segmentsToUse.join("/");
3594
- rd.folders = [
3595
- {
3596
- fullyQualifiedName: folderFqn
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
- } else
3601
- rd.folders = [
3602
- {
3603
- fullyQualifiedName: SolutionConstants.ROOT_FOLDER_NAME
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
- resources.push(rd);
3607
- const resourceKey = rd.key;
3608
- const normalizedFullPath = this.getNormalizedPath(resourceFile);
3609
- if (!pathsByKind.has(rd.kind))
3610
- pathsByKind.set(rd.kind, []);
3611
- const kindPaths = pathsByKind.get(rd.kind);
3612
- if (kindPaths)
3613
- kindPaths.push({
3614
- name: rd.name,
3615
- key: rd.key,
3616
- path: normalizedFullPath
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
- if (missingFiles.length > 0)
3642
- throw new BadRequestException(ErrorCodes.Resources.MissingFileDependencies, `Solution has missing files: ${missingFiles.join(", ")}`);
3643
- if (invalidFiles.length > 0)
3644
- throw new BadRequestException(ErrorCodes.Resources.InvalidResourceFile, `Solution has resource files with invalid content: ${invalidFiles.join(", ")}`);
3645
- if (parseErrors.length > 0)
3646
- throw new Error(`Failed to read resource files for solution ${context.path}: ${parseErrors.join(", ")}`);
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
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
- await this.replaceDuplicateKeysAsync(solution, cancellationToken);
3676
- if (!context.isReadOnly && tree) {
3677
- const orderedResourceKeys = getTopologicalIdsOrdering(buildDependencyGraph(solution.resources));
3678
- tree.push(...orderedResourceKeys);
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
- return solution;
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-rf2g1kfj.js"),
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 jsonObject = JSON.parse(cleanInput);
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 (!versionValue)
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
- const versionParts = versionValue.split(".").map(Number);
4370
- const majorVersion = versionParts[0];
4371
- if (majorVersion !== 2)
4372
- throw new Error("Unsupported bindings schema");
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 = jsonObject[BindingsKnownKeys.Resources];
4378
- if (Array.isArray(resources))
4379
- for (const resourceElement of resources) {
4380
- if (resourceElement == null) {
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 isBusinessRule = bindingResource.binding.resourceType === models_BindingsResourceType.BusinessRule;
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(_request, cancellationToken) {
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
- return await orchestratorClient.getPublishLocationsAsync(cancellationToken);
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 (!(!metadata || isAnyReferenceProperty(metadata)) && metadata.readOnly !== true) {
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
- // ../../node_modules/@uipath/resource-builder-sdk/dist/handlers/solution-handlers.js
9008
- var DUPLICATE_NAMES_SUPPORTED_KINDS = new Set([
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: normalizedBaseUrl,
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
- const url = `${this.options.baseUrl}api/v2/AutomationSolutions/publish_locations`;
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
- // ../solution-sdk/src/resources/services/project-artifacts-service.ts
15742
- async function addProjectArtifactsToSolutionAsync(options) {
15743
- const error = await createProjectInSolutionBuilder(options.solutionDir, options.projectId, options.projectName, normalizeProjectType(options.projectType), options.projectSubType);
15744
- return error ? artifactFailure(options, error) : { Created: true };
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 artifactFailure(options, error) {
15747
- 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.");
15748
- return { Created: false, Error: error.message };
16218
+ function stopsCatalogLookups(failure) {
16219
+ return failure.kind !== "failed";
15749
16220
  }
15750
-
15751
- // ../solution-sdk/src/resources/services/project-template-types.ts
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 hasAnyTemplateRow(projectType) {
15758
- reader ??= new EmbeddedProjectConfigurationReader;
15759
- const wanted = projectType.toLowerCase();
15760
- return Object.keys(reader.getAll()).some((row) => {
15761
- const key = row.toLowerCase();
15762
- return key === wanted || key.startsWith(`${wanted}_`);
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
- // ../solution-sdk/src/resources/services/resource-casing.ts
15826
- var RESOURCE_FILES_FOLDER_NAME = "resources";
15827
- function lowerFirstChar(value) {
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 normalizeResourceCasing(solution) {
15831
- let normalized = 0;
15832
- for (const resource of solution?.resources ?? []) {
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
- async function saveCasingNormalized(builder) {
15844
- const [loadError, solution] = await catchError(builder.context.loadAsync());
15845
- if (loadError) {
15846
- logger.warn(`Skipping resource casing normalization; failed to load solution: ${loadError.message}`);
15847
- } else {
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
- async function normalizeResourceFileCasing(solutionDir) {
15854
- const fs = getFileSystem();
15855
- const root = fs.path.join(solutionDir, RESOURCE_FILES_FOLDER_NAME);
15856
- if (!await fs.exists(root))
15857
- return [];
15858
- const byTarget = new Map;
15859
- for (const [path, target] of await collectResourceFiles(root, [])) {
15860
- byTarget.set(target, [...byTarget.get(target) ?? [], path]);
15861
- }
15862
- const changed = [];
15863
- for (const [target, paths] of byTarget) {
15864
- const keep = paths.includes(target) ? target : paths[0];
15865
- for (const path of paths) {
15866
- if (path === keep)
15867
- continue;
15868
- const [removeError] = await catchError(fs.rm(path));
15869
- if (removeError) {
15870
- logger.warn(`Could not remove case-variant resource file ${path}: ${removeError.message}`);
15871
- continue;
15872
- }
15873
- changed.push(path);
15874
- logger.info(`Removed case-variant resource file ${path}; ${keep} holds the same resource`);
15875
- }
15876
- if (keep === target)
15877
- continue;
15878
- if (keep.toLowerCase() === target.toLowerCase() && await fs.exists(target)) {
15879
- continue;
15880
- }
15881
- const [makeDirError] = await catchError(fs.mkdir(fs.path.dirname(target)));
15882
- if (makeDirError) {
15883
- logger.warn(`Could not create directory for resource file ${target}: ${makeDirError.message}`);
15884
- continue;
15885
- }
15886
- const [renameError] = await catchError(fs.rename(keep, target));
15887
- if (renameError) {
15888
- logger.warn(`Could not move resource file ${keep} to ${target}: ${renameError.message}`);
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] === kind)
16555
+ if (sameIgnoringCase(target[kindIndex], kind)) {
15904
16556
  target[kindIndex] = lowerFirstChar(kind);
15905
- if (type && target[kindIndex + 1] === type) {
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
- warnings: []
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, bindingsJsonFallbackDirs, serializer);
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 rcsMatch = directCloudKey ? await findResourceByKeyInRcs(builder, kind, directCloudKey, folderPath) : await findResourceInRcs(builder, kind, name, folderPath);
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 [importError] = await catchError(builder.addOrUpdateResourceToSolutionAsync({
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
- }, true));
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
- result.warnings.push(`Failed to import connection ${directCloudKey}: ${importError.message}`);
16206
- logger.warn(`Failed to import connection ${directCloudKey}: ${importError.message}`);
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
- logger.warn(`Failed to import ${kind} "${name}" from Orchestrator: ${importError.message}. Will create locally instead.`);
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
- async function collectBindings(solutionDir, projectPaths, bindingsJsonFallbackDirs, serializer) {
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 candidateDirs = [solutionDir, ...projectPaths];
16592
- for (const dir of candidateDirs) {
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 decoded = new TextDecoder().decode(content);
16612
- let success;
16613
- let metadata;
16614
- try {
16615
- const result = serializer.tryDeserialize(decoded);
16616
- success = result.success;
16617
- metadata = result.metadata;
16618
- } catch (err) {
16619
- parseErrors.push({
16620
- path: bindingsPath,
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
- continue;
16624
- }
16625
- if (!success) {
16626
- parseErrors.push({
16627
- path: bindingsPath,
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
- continue;
16631
- }
16632
- if (!metadata?.resources?.length) {
16633
- continue;
16634
- }
16635
- allBindings.push(...metadata.resources);
17601
+ }
17602
+ };
16636
17603
  }
16637
- return { bindings: allBindings, parseErrors };
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 [syncError, result] = await catchError(syncResourcesFromBindings(resolvedPath, undefined, projectSubTypes ?? new Map));
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=503E17628305A6E964756E2164756E21
17860
+ //# debugId=D96A40C680DB2DF664756E2164756E21