@adobe/aio-commerce-lib-app 2.0.0-beta-20260820174436 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +31 -5
  2. package/dist/cjs/actions/app-config/index.cjs +3 -3
  3. package/dist/cjs/actions/app-config/index.d.cts +3 -4
  4. package/dist/cjs/actions/association/index.d.cts +2 -3
  5. package/dist/cjs/actions/config/index.d.cts +2 -3
  6. package/dist/cjs/actions/installation/index.cjs +14 -7
  7. package/dist/cjs/actions/installation/index.d.cts +5 -5
  8. package/dist/cjs/actions/scope-tree/index.d.cts +2 -3
  9. package/dist/cjs/{app-Cf3EmtUh.d.cts → app-DwSqUkJA.d.cts} +122 -16
  10. package/dist/cjs/commands/index.cjs +91 -51
  11. package/dist/cjs/commands/templates/admin-ui/web-src/.babelrc +14 -0
  12. package/dist/cjs/commands/templates/business-configuration/config.js.template +7 -1
  13. package/dist/cjs/{common-B70f39vI.cjs → common-VSsQEHDv.cjs} +15 -2
  14. package/dist/cjs/config/index.browser.d.cts +1 -1
  15. package/dist/cjs/config/index.node.cjs +4 -3
  16. package/dist/cjs/config/index.node.d.cts +104 -50
  17. package/dist/{es/define-DSU8Kgu0.d.mts → cjs/define-Cc3AaVmY.d.cts} +1 -1
  18. package/dist/{es/index-COLnI6Ok.d.mts → cjs/index-C7dqTDHu.d.cts} +25 -3
  19. package/dist/cjs/index.d.cts +10 -10
  20. package/dist/cjs/{index.node-CXeIG8SS.cjs → index.node-vcOo1rt0.cjs} +1 -1
  21. package/dist/cjs/installation-D72LoQ_1.d.cts +39 -0
  22. package/dist/cjs/management/index.cjs +1 -1
  23. package/dist/cjs/management/index.d.cts +1 -1
  24. package/dist/cjs/{management-BhMdRr8i.cjs → management-Bki3hlxL.cjs} +207 -27
  25. package/dist/cjs/{openapi-DVJUTOXY.cjs → openapi-CAXgKam0.cjs} +317 -390
  26. package/dist/cjs/{validate-Dff0nrqM.cjs → validate-CwWYtRsS.cjs} +33 -2
  27. package/dist/cjs/{webhooks-E1KDx3Yu.cjs → webhooks-Bcq2Qy7q.cjs} +69 -8
  28. package/dist/es/actions/app-config/index.d.mts +3 -4
  29. package/dist/es/actions/app-config/index.mjs +3 -3
  30. package/dist/es/actions/association/index.d.mts +2 -3
  31. package/dist/es/actions/config/index.d.mts +2 -3
  32. package/dist/es/actions/installation/index.d.mts +5 -5
  33. package/dist/es/actions/installation/index.mjs +14 -7
  34. package/dist/es/actions/scope-tree/index.d.mts +2 -3
  35. package/dist/es/{app-Cf3EmtUh.d.mts → app-DwSqUkJA.d.mts} +122 -16
  36. package/dist/es/commands/index.mjs +92 -52
  37. package/dist/es/commands/templates/admin-ui/web-src/.babelrc +14 -0
  38. package/dist/es/commands/templates/business-configuration/config.js.template +7 -1
  39. package/dist/es/{common-BVG9tiX0.mjs → common-dy6NrZt8.mjs} +10 -3
  40. package/dist/es/config/index.browser.d.mts +1 -1
  41. package/dist/es/config/index.node.d.mts +104 -50
  42. package/dist/es/config/index.node.mjs +4 -4
  43. package/dist/{cjs/define-B-_oKmCe.d.cts → es/define-Bq1zw9Lr.d.mts} +1 -1
  44. package/dist/{cjs/index-CkwCGvut.d.cts → es/index-BryddQId.d.mts} +25 -3
  45. package/dist/es/index.d.mts +10 -10
  46. package/dist/es/{index.node-DRTF5Hue.mjs → index.node-CW46zuxW.mjs} +1 -1
  47. package/dist/es/installation-DqVDJ1ur.d.mts +39 -0
  48. package/dist/es/management/index.d.mts +1 -1
  49. package/dist/es/management/index.mjs +1 -1
  50. package/dist/es/{management-BTWQdJNO.mjs → management-DgwAhaaJ.mjs} +207 -27
  51. package/dist/es/{openapi-D18LT0Wp.mjs → openapi-BnR9OUac.mjs} +317 -390
  52. package/dist/es/{validate-8AXMGEwr.mjs → validate-NzW1FQwL.mjs} +29 -4
  53. package/dist/es/{webhooks-BQ8kSN2Z.mjs → webhooks-CHZ1SXxw.mjs} +64 -9
  54. package/package.json +19 -19
@@ -15,17 +15,17 @@
15
15
  import { C as makeWorkspaceConfig, S as groupEventsByRuntimeActions, T as sanitizeEventingIdentifier, _ as getNamespacedEvent, a as eventCodeOf, b as getRegistrationName, c as findExistingRegistrations, d as generateInstanceIdDeprecated, f as getCommerceEventingConfigurationUpdateParams, g as getLegacyRegistrationName, h as getIoEventsExistingData, i as diffByKey, l as findExistingSubscription, m as getIoEventCode, n as EVENTS_STORAGE_KEY, o as findExistingProvider, p as getCommerceEventingExistingData, r as EXTERNAL_PROVIDER_TYPE, s as findExistingProviderMetadata, t as COMMERCE_PROVIDER_TYPE, u as generateInstanceId, v as getProviderKey, w as removeStoredEventProviders, x as getSubscriptionChangeKind, y as getRegistrationDescription } from "./utils-DgpVB4lT.mjs";
16
16
  import { t as stringifyError } from "./error-_RMwtrUA.mjs";
17
17
  import { a as parseOrThrow, i as getInstallCommerceEnv, t as appliesToEnv } from "./environment-C84BPRJZ.mjs";
18
- import { a as hasCustomInstallationSteps, c as hasEventing, d as hasAdminUi, l as hasExternalEvents, n as hasWebhooks, s as hasCommerceEvents } from "./webhooks-BQ8kSN2Z.mjs";
18
+ import { a as hasCustomInstallationSteps, c as hasEventing, d as hasAdminUi, l as hasExternalEvents, n as hasWebhooks, s as hasCommerceEvents } from "./webhooks-CHZ1SXxw.mjs";
19
19
  import { n as LIFECYCLE_HTTP_CLIENT_TIMEOUT_MS, t as inspect } from "./logging-CXesop3u.mjs";
20
20
  import { resolveCommerceHttpClientParams, resolveIoEventsHttpClientParams } from "@adobe/aio-commerce-lib-api";
21
21
  import { getSystemConfigByKey, setSystemConfigByKey } from "@adobe/aio-commerce-lib-config";
22
22
  import { resolveImsAuthParams } from "@adobe/aio-commerce-lib-auth";
23
23
  import stringify, { stringify as stringify$1 } from "safe-stable-stringify";
24
24
  import * as v from "valibot";
25
+ import { createAdminUiApiClient } from "@adobe/aio-commerce-lib-admin-ui/api";
25
26
  import { HTTPError } from "ky";
26
27
  import { HTTP_NOT_FOUND, unwrapHttpError } from "@adobe/aio-commerce-lib-api/utils";
27
28
  import * as camelcaseModule from "camelcase";
28
- import { createAdminUiApiClient } from "@adobe/aio-commerce-lib-admin-ui/api";
29
29
  import { createCustomCommerceEventsApiClient, createEventProvider, createEventSubscription, deleteEventProvider, deleteEventSubscription, getAllEventProviders, getAllEventSubscriptions, updateEventSubscription, updateEventingConfiguration } from "@adobe/aio-commerce-lib-events/commerce";
30
30
  import { createCustomAdobeIoEventsApiClient, createEventMetadataForProvider, createEventProvider as createEventProvider$1, createRegistration, deleteEventMetadataForProvider, deleteEventProvider as deleteEventProvider$1, deleteRegistration, getAllEventProviders as getAllEventProviders$1, getAllRegistrations, updateRegistration } from "@adobe/aio-commerce-lib-events/io-events";
31
31
  import { createCustomCommerceWebhooksApiClient, getWebhookList, subscribeWebhook, unsubscribeWebhook } from "@adobe/aio-commerce-lib-webhooks/api";
@@ -511,6 +511,18 @@ function resolveCustomScriptHandler(scriptModule, handler) {
511
511
  return null;
512
512
  }
513
513
  /**
514
+ * Resolves a script module from the customScripts context, throwing when it's absent.
515
+ *
516
+ * @param customScripts - The customScripts context containing the loaded modules.
517
+ * @param script - The script path to resolve the module for.
518
+ * @throws If the script is not present in the context.
519
+ */
520
+ function getScriptModuleOrThrow(customScripts, script) {
521
+ const scriptModule = getScriptModule(customScripts, script);
522
+ if (!scriptModule) throw new Error(`Script ${script} not found in customScripts context. Make sure the script is defined in the configuration and the action was generated with custom scripts support.`);
523
+ return scriptModule;
524
+ }
525
+ /**
514
526
  * Creates a leaf step for executing a single custom installation script.
515
527
  */
516
528
  function createCustomScriptStep(scriptConfig) {
@@ -521,9 +533,7 @@ function createCustomScriptStep(scriptConfig) {
521
533
  const customScripts = context.customScripts || {};
522
534
  logger.info(`Executing custom installation script: ${name}`);
523
535
  logger.debug(`Script path: ${script}`);
524
- const scriptModule = getScriptModule(customScripts, script);
525
- if (!scriptModule) throw new Error(`Script ${script} not found in customScripts context. Make sure the script is defined in the configuration and the action was generated with custom scripts support.`);
526
- const scriptResult = await resolveCustomScriptHandler(scriptModule, "install")(config, context);
536
+ const scriptResult = await resolveCustomScriptHandler(getScriptModuleOrThrow(customScripts, script), "install")(config, context);
527
537
  logger.info(`Successfully executed script: ${name}`);
528
538
  return {
529
539
  data: scriptResult,
@@ -565,8 +575,134 @@ function createCustomScriptSteps(config) {
565
575
  return steps.map((scriptConfig) => createCustomScriptStep(scriptConfig));
566
576
  }
567
577
 
578
+ //#endregion
579
+ //#region source/management/domains/custom-installation/apply.ts
580
+ /** Runs a newly-added step's `install` handler and returns its persisted identity. */
581
+ async function runAddedStep(step, targetConfig, context) {
582
+ const { logger } = context;
583
+ const scriptModule = getScriptModuleOrThrow(context.customScripts ?? {}, step.script);
584
+ logger.info(`Executing custom installation script: ${step.name}`);
585
+ await resolveCustomScriptHandler(scriptModule, "install")(targetConfig, context);
586
+ logger.info(`Successfully executed script: ${step.name}`);
587
+ return {
588
+ name: step.name,
589
+ script: step.script
590
+ };
591
+ }
592
+ /**
593
+ * Applies the plan: runs `install` for first-time steps, warns about retained steps that point to
594
+ * a different script (without re-running them), and ignores steps no longer in the config. Returns
595
+ * the baseline history with the newly-added steps appended.
596
+ */
597
+ async function applyCustomInstallationSteps(plan, context) {
598
+ const { baselineExecutedSteps, targetConfig, operations } = plan;
599
+ if (!targetConfig) return { snapshotData: { executedSteps: baselineExecutedSteps } };
600
+ const targetSteps = hasCustomInstallationSteps(targetConfig) ? targetConfig.installation.customInstallationSteps : [];
601
+ const addedNames = new Set(operations.filter((op) => op.kind === "add").map((op) => op.after.name));
602
+ for (const targetStep of targetSteps) {
603
+ if (addedNames.has(targetStep.name)) continue;
604
+ const baselineStep = baselineExecutedSteps.find((step) => step.name === targetStep.name);
605
+ if (baselineStep && baselineStep.script !== targetStep.script) context.logger.warn(`Custom installation step "${targetStep.name}" now points to a different script ("${targetStep.script}" instead of "${baselineStep.script}"). This does not affect append-only behavior: its install already ran and will not run again.`);
606
+ }
607
+ const addedSteps = targetSteps.filter((step) => addedNames.has(step.name));
608
+ const addedIdentities = [];
609
+ for (const step of addedSteps) addedIdentities.push(await runAddedStep(step, targetConfig, context));
610
+ return { snapshotData: { executedSteps: [...baselineExecutedSteps, ...addedIdentities] } };
611
+ }
612
+
613
+ //#endregion
614
+ //#region source/management/domains/custom-installation/plan.ts
615
+ /** Builds an `add` operation for a step run for the first time. */
616
+ function buildAddOperation(step) {
617
+ return {
618
+ after: {
619
+ name: step.name,
620
+ script: step.script
621
+ },
622
+ id: `add:${step.name}`,
623
+ kind: "add",
624
+ label: `Run custom installation step "${step.name}"`
625
+ };
626
+ }
627
+ /**
628
+ * Builds an informational `remove` operation for a step no longer in the target config. No
629
+ * `uninstall` runs from it; it exists so the plan reflects that the step left the configuration.
630
+ */
631
+ function buildRemoveOperation(step) {
632
+ return {
633
+ before: {
634
+ name: step.name,
635
+ script: step.script
636
+ },
637
+ id: `remove:${step.name}`,
638
+ kind: "remove",
639
+ label: `Custom installation step "${step.name}" no longer in the configuration`
640
+ };
641
+ }
642
+ /**
643
+ * Resolves the executed-step history to diff the target against. Prefers the reconciliation
644
+ * snapshot when present, and otherwise reconstructs it from the baseline config's own steps.
645
+ */
646
+ function resolveBaselineExecutedSteps(baseline) {
647
+ const recorded = baseline?.data?.executedSteps;
648
+ if (recorded) return recorded;
649
+ if (baseline && hasCustomInstallationSteps(baseline.config)) return baseline.config.installation.customInstallationSteps.map((step) => ({
650
+ name: step.name,
651
+ script: step.script
652
+ }));
653
+ return [];
654
+ }
655
+ /**
656
+ * Plans the custom installation steps domain by diffing the baseline's executed-step history
657
+ * against the target config's steps, by `name`: `add` for names not seen before, `remove` for
658
+ * names no longer configured. Steps present in both produce no operation and are never re-run.
659
+ */
660
+ function planCustomInstallationSteps(input, _context) {
661
+ const { path, baseline, targetConfig } = input;
662
+ const baselineExecutedSteps = resolveBaselineExecutedSteps(baseline);
663
+ const targetSteps = targetConfig && hasCustomInstallationSteps(targetConfig) ? targetConfig.installation.customInstallationSteps : [];
664
+ const baselineNames = new Set(baselineExecutedSteps.map((s) => s.name));
665
+ const targetNames = new Set(targetSteps.map((s) => s.name));
666
+ const operations = [...targetSteps.filter((step) => !baselineNames.has(step.name)).map((step) => buildAddOperation(step)), ...baselineExecutedSteps.filter((step) => !targetNames.has(step.name)).map((step) => buildRemoveOperation(step))];
667
+ return Promise.resolve({
668
+ kind: "planned",
669
+ plan: {
670
+ baselineExecutedSteps,
671
+ operations,
672
+ path,
673
+ targetConfig
674
+ }
675
+ });
676
+ }
677
+
568
678
  //#endregion
569
679
  //#region source/management/domains/custom-installation/branch.ts
680
+ /**
681
+ * Leaf step that reconciles the custom installation steps domain as a whole via `plan`/`apply`.
682
+ * It participates only in the upgrade tree; the per-script leaves handle install and uninstall.
683
+ */
684
+ const reconciliationStep = defineLeafStep({
685
+ apply: applyCustomInstallationSteps,
686
+ install: (config) => {
687
+ if (!hasCustomInstallationSteps(config)) return { executedSteps: [] };
688
+ return { executedSteps: config.installation.customInstallationSteps.map((step) => ({
689
+ name: step.name,
690
+ script: step.script
691
+ })) };
692
+ },
693
+ meta: {
694
+ install: {
695
+ description: "Records which custom installation steps ran, so future upgrades can detect additions and removals",
696
+ label: "Reconcile Custom Installation Steps"
697
+ },
698
+ upgrade: {
699
+ description: "Runs custom installation steps added since the last version",
700
+ label: "Reconcile Custom Installation Steps"
701
+ }
702
+ },
703
+ name: "reconciliation",
704
+ plan: planCustomInstallationSteps
705
+ });
570
706
  /** Root custom installation step that executes custom installation scripts. */
571
707
  const customInstallationStepBase = defineBranchStep({
572
708
  children: [],
@@ -579,18 +715,33 @@ const customInstallationStepBase = defineBranchStep({
579
715
  uninstall: {
580
716
  description: "Executes custom uninstallation scripts defined in the application configuration",
581
717
  label: "Custom Uninstallation Steps"
718
+ },
719
+ upgrade: {
720
+ description: "Reconciles custom installation steps added or removed between versions",
721
+ label: "Custom Installation Steps"
582
722
  }
583
723
  },
584
724
  name: "customInstallationSteps"
585
725
  });
586
726
  /**
587
- * Creates the custom installation step with dynamic children based on config.
588
- * Each custom script becomes a direct child step.
589
- */
590
- function createCustomInstallationStep(config) {
727
+ * Creates the custom installation step with dynamic children based on config. `executedSteps` is
728
+ * the recorded run history, only passed when building the full-uninstall tree.
729
+ * `includeReconciliation` adds the reconciliation leaf, which only runs on upgrade; install and
730
+ * uninstall leave it out.
731
+ */
732
+ function createCustomInstallationStep(config, executedSteps = [], includeReconciliation = false) {
733
+ const children = executedSteps.length > 0 ? [...executedSteps].reverse().map((identity) => {
734
+ const currentStep = hasCustomInstallationSteps(config) ? config.installation.customInstallationSteps.find((step) => step.name === identity.name) : void 0;
735
+ return createCustomScriptStep(currentStep ?? {
736
+ description: "Previously executed custom installation step, no longer present in the configuration.",
737
+ name: identity.name,
738
+ script: identity.script
739
+ });
740
+ }) : [...createCustomScriptSteps(config), ...includeReconciliation ? [reconciliationStep] : []];
591
741
  return {
592
742
  ...customInstallationStepBase,
593
- children: createCustomScriptSteps(config)
743
+ children,
744
+ isConfigured: (candidateConfig) => hasCustomInstallationSteps(candidateConfig) || executedSteps.length > 0
594
745
  };
595
746
  }
596
747
 
@@ -841,6 +992,18 @@ function enumerateComponents(adminUi) {
841
992
  label: "menu",
842
993
  ref: { kind: "menu" }
843
994
  });
995
+ for (const aclEntry of adminUi.acl ?? []) {
996
+ const key = `acl.${aclEntry.id}`;
997
+ components.set(key, {
998
+ config: aclEntry,
999
+ key,
1000
+ label: `acl resource "${aclEntry.id}"`,
1001
+ ref: {
1002
+ id: aclEntry.id,
1003
+ kind: "acl"
1004
+ }
1005
+ });
1006
+ }
844
1007
  for (const entity of ADMIN_UI_ENTITIES) {
845
1008
  const entityConfig = adminUi[entity];
846
1009
  if (!entityConfig) continue;
@@ -1116,15 +1279,24 @@ async function reconcileProviderSubResources(baseline, target, targetMetadata, b
1116
1279
  }
1117
1280
  await removeDroppedMetadata(providerData, options.type, target.events, baseline.events, targetMetadata, baselineMetadata, context);
1118
1281
  }
1119
- /** Finds the deployed I/O Events provider by its current or legacy instance id. */
1282
+ /**
1283
+ * Finds the deployed I/O Events provider for a target, preferring the current workspace-scoped
1284
+ * instance id over the legacy (workspace-less) one, and a metadata-bearing provider over an empty
1285
+ * duplicate that shares its instance id.
1286
+ */
1120
1287
  function resolveDeployedProvider(target, targetMetadata, baselineMetadata, workspaceId, existingData) {
1121
- const candidates = /* @__PURE__ */ new Set([
1288
+ const orderedCandidates = [
1122
1289
  generateInstanceId(targetMetadata, target.provider, workspaceId),
1123
- generateInstanceIdDeprecated(targetMetadata, target.provider),
1124
1290
  generateInstanceId(baselineMetadata, target.provider, workspaceId),
1291
+ generateInstanceIdDeprecated(targetMetadata, target.provider),
1125
1292
  generateInstanceIdDeprecated(baselineMetadata, target.provider)
1126
- ]);
1127
- return existingData.providersWithMetadata.find((candidate) => candidates.has(candidate.instance_id)) ?? null;
1293
+ ];
1294
+ for (const candidate of orderedCandidates) {
1295
+ const matches = existingData.providersWithMetadata.filter((provider) => provider.instance_id === candidate);
1296
+ if (matches.length === 0) continue;
1297
+ return matches.find((provider) => provider.metadata.length > 0) ?? matches[0];
1298
+ }
1299
+ return null;
1128
1300
  }
1129
1301
  /** The fully-qualified I/O Events code set for a group of events under a provider type. */
1130
1302
  function eventCodeSet(events, type, metadata) {
@@ -2941,22 +3113,26 @@ const webhooksStep = defineBranchStep({
2941
3113
  //#endregion
2942
3114
  //#region source/management/installation/root.ts
2943
3115
  /**
2944
- * Creates the default child steps built-in in the library with dynamic children based on the config.
3116
+ * Creates the default child steps built-in in the library with dynamic children based on the
3117
+ * config. `executedCustomInstallationSteps` is only meaningful for a full uninstall (see
3118
+ * {@link createRootUninstallationStep}). `includeReconciliation` adds the reconciliation leaf,
3119
+ * which only runs on upgrade.
2945
3120
  */
2946
- function createDefaultChildSteps(config) {
3121
+ function createDefaultChildSteps(config, executedCustomInstallationSteps = [], includeReconciliation = false) {
2947
3122
  return [
2948
3123
  eventingStep,
2949
3124
  webhooksStep,
2950
3125
  adminUiStep,
2951
- createCustomInstallationStep(config)
3126
+ createCustomInstallationStep(config, executedCustomInstallationSteps, includeReconciliation)
2952
3127
  ];
2953
3128
  }
2954
3129
  /**
2955
- * Creates a root installation step with dynamic children based on the config.
3130
+ * Creates a root installation step with dynamic children based on the config. `forUpgrade` adds the
3131
+ * reconciliation leaf, which only runs on upgrade.
2956
3132
  */
2957
- function createRootInstallationStep(config) {
3133
+ function createRootInstallationStep(config, { forUpgrade = false } = {}) {
2958
3134
  return defineBranchStep({
2959
- children: createDefaultChildSteps(config),
3135
+ children: createDefaultChildSteps(config, [], forUpgrade),
2960
3136
  meta: {
2961
3137
  install: {
2962
3138
  description: "App installation workflow",
@@ -2976,10 +3152,14 @@ function createRootInstallationStep(config) {
2976
3152
  }
2977
3153
  /**
2978
3154
  * Creates a root uninstallation step with dynamic children based on the config.
3155
+ *
3156
+ * `executedCustomInstallationSteps` is the persisted history of every custom installation step
3157
+ * that ever ran (from the lifecycle baseline snapshot). Passing it lets a full unassociate reach
3158
+ * steps that ran in a previous version but were since removed from the config.
2979
3159
  */
2980
- function createRootUninstallationStep(config) {
3160
+ function createRootUninstallationStep(config, executedCustomInstallationSteps = []) {
2981
3161
  return defineBranchStep({
2982
- children: createDefaultChildSteps(config),
3162
+ children: createDefaultChildSteps(config, executedCustomInstallationSteps),
2983
3163
  meta: { install: {
2984
3164
  description: "App uninstallation workflow",
2985
3165
  label: "Uninstallation"
@@ -3067,8 +3247,8 @@ async function runInstallation(options) {
3067
3247
  * Creates an initial uninstallation state from the config and step definitions.
3068
3248
  */
3069
3249
  function createInitialUninstallationState(options) {
3070
- const { config } = options;
3071
- const rootStep = createRootUninstallationStep(config);
3250
+ const { config, executedCustomInstallationSteps = [] } = options;
3251
+ const rootStep = createRootUninstallationStep(config, executedCustomInstallationSteps);
3072
3252
  return createInitialState({
3073
3253
  config,
3074
3254
  mode: "uninstall",
@@ -3079,8 +3259,8 @@ function createInitialUninstallationState(options) {
3079
3259
  * Runs the full uninstallation workflow. Returns the final state (never throws).
3080
3260
  */
3081
3261
  function runUninstallation(options) {
3082
- const { installationContext, config, initialState, hooks } = options;
3083
- const rootStep = createRootUninstallationStep(config);
3262
+ const { installationContext, config, initialState, hooks, executedCustomInstallationSteps = [] } = options;
3263
+ const rootStep = createRootUninstallationStep(config, executedCustomInstallationSteps);
3084
3264
  return executeUninstallWorkflow({
3085
3265
  config,
3086
3266
  failureKey: "INSTALLATION_FAILED",