@adobe/aio-commerce-lib-app 1.2.0 → 1.3.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.
@@ -57,7 +57,11 @@ router.get("/", {
57
57
  } });
58
58
  }
59
59
  });
60
- /** POST / - Set configuration */
60
+ /**
61
+ * PUT / - Set configuration (deprecated)
62
+ * @deprecated Use PATCH instead. This endpoint overwrites all values for the scope
63
+ * and does not support partial updates or unset semantics.
64
+ */
61
65
  router.put("/", {
62
66
  body: v.object({
63
67
  scopeId: nonEmptyStringValueSchema("scopeId"),
@@ -71,8 +75,35 @@ router.put("/", {
71
75
  const { configSchema } = rawParams;
72
76
  logger.debug(`Setting configuration with scope id: ${req.body.scopeId}`);
73
77
  const { scopeId, config } = req.body;
78
+ initialize({ schema: validateCommerceAppConfigDomain(configSchema, "businessConfig.schema") });
74
79
  const result = await setConfiguration({ config: config.filter((item) => item.value !== MASKED_PASSWORD_VALUE) }, byScopeId(scopeId), { encryptionKey: rawParams.AIO_COMMERCE_CONFIG_ENCRYPTION_KEY });
75
80
  result.config = filterPasswordFields(configSchema, result.config);
81
+ return ok({
82
+ body: result,
83
+ headers: {
84
+ "Cache-Control": "no-store",
85
+ Deprecation: "Wed, 15 Apr 2026 00:00:00 GMT"
86
+ }
87
+ });
88
+ }
89
+ });
90
+ /** PATCH / - Partially update configuration */
91
+ router.patch("/", {
92
+ body: v.object({
93
+ scopeId: nonEmptyStringValueSchema("scopeId"),
94
+ config: v.array(v.object({
95
+ name: nonEmptyStringValueSchema("config.name"),
96
+ value: v.nullable(v.union([v.string(), v.array(v.string())]))
97
+ }))
98
+ }),
99
+ handler: async (req, ctx) => {
100
+ const { logger, rawParams } = ctx;
101
+ const { configSchema } = rawParams;
102
+ logger.debug(`Patching configuration with scope id: ${req.body.scopeId}`);
103
+ const { scopeId, config } = req.body;
104
+ initialize({ schema: validateCommerceAppConfigDomain(configSchema, "businessConfig.schema") });
105
+ const result = await setConfiguration({ config }, byScopeId(scopeId), { encryptionKey: rawParams.AIO_COMMERCE_CONFIG_ENCRYPTION_KEY });
106
+ result.config = filterPasswordFields(configSchema, result.config);
76
107
  return ok({
77
108
  body: result,
78
109
  headers: { "Cache-Control": "no-store" }
@@ -13,7 +13,7 @@
13
13
  */
14
14
 
15
15
  import { n as CommerceAppConfigOutputModel } from "../app-DJr-mN9d.mjs";
16
- import { V as InstallationContext } from "../index-BmYXe7kp.mjs";
16
+ import { q as InstallationContext } from "../index-BENO5T7n.mjs";
17
17
  import * as _$_adobe_aio_commerce_lib_core_responses0 from "@adobe/aio-commerce-lib-core/responses";
18
18
  import { RuntimeActionParams } from "@adobe/aio-commerce-lib-core/params";
19
19
 
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { n as logger, t as HttpActionRouter } from "../router-BxaxEEu3.mjs";
16
16
  import { r as nonEmptyStringValueSchema } from "../schemas-CVXHgUhv.mjs";
17
- import { a as isCompletedState, c as isSucceededState, n as runInstallation, o as isFailedState, r as runValidation, s as isInProgressState, t as createInitialInstallationState } from "../management-DSexEPTW.mjs";
17
+ import { a as runValidation, c as isFailedState, i as runUninstallation, l as isInProgressState, n as createInitialUninstallationState, r as runInstallation, s as isCompletedState, t as createInitialInstallationState, u as isSucceededState } from "../management-ByHvVJ12.mjs";
18
18
  import { accepted, badRequest, conflict, internalServerError, noContent, ok } from "@adobe/aio-commerce-lib-core/responses";
19
19
  import * as v from "valibot";
20
20
  import { object, string } from "valibot";
@@ -266,21 +266,68 @@ const InstallationRequestBodySchema = object({
266
266
  ioEventsUrl: string(),
267
267
  ioEventsEnv: string()
268
268
  });
269
- /** Creates an installation state store using lib-core combined storage. */
270
- function createInstallationStore() {
269
+ /** Creates a workflow state store with the given prefix. */
270
+ function createWorkflowStore(prefix) {
271
271
  return createCombinedStore({
272
- cache: { keyPrefix: "installation" },
272
+ cache: { keyPrefix: prefix },
273
273
  persistent: {
274
- dirPrefix: "installation",
274
+ dirPrefix: prefix,
275
275
  shouldPersist: isCompletedState
276
276
  }
277
277
  });
278
278
  }
279
+ /** Creates the installation state store. */
280
+ function createInstallationStore() {
281
+ return createWorkflowStore("installation");
282
+ }
283
+ /** Creates the uninstallation state store. */
284
+ function createUninstallationStore() {
285
+ return createWorkflowStore("uninstallation");
286
+ }
279
287
  /** Returns the storage key used to store the current installation ID. */
280
288
  function getStorageKey() {
281
289
  return "current";
282
290
  }
283
291
  /**
292
+ * Merges rawParams with body fields, overriding API URLs.
293
+ * Shared by POST /, POST /execution, POST /uninstallation, POST /uninstallation/execution.
294
+ */
295
+ function buildWorkflowParams(body, rawParams) {
296
+ return {
297
+ ...rawParams,
298
+ appData: body.appData,
299
+ AIO_EVENTS_API_BASE_URL: body.ioEventsUrl,
300
+ AIO_COMMERCE_AUTH_IMS_ENVIRONMENT: body.ioEventsEnv,
301
+ AIO_COMMERCE_API_BASE_URL: body.commerceBaseUrl,
302
+ AIO_COMMERCE_API_FLAVOR: body.commerceEnv
303
+ };
304
+ }
305
+ /**
306
+ * Builds an InstallationContext from merged workflow params.
307
+ * Shared by POST /execution and POST /uninstallation/execution.
308
+ */
309
+ function buildInstallationContext(params, appConfig, logFn) {
310
+ return {
311
+ appData: params.appData,
312
+ params,
313
+ logger: logFn,
314
+ customScripts: params.customScriptsLoader?.(appConfig, logFn) ?? {}
315
+ };
316
+ }
317
+ /**
318
+ * Reads state from a store and returns 200 with body or 204.
319
+ * Shared by GET / and GET /uninstallation.
320
+ */
321
+ async function readStateFromStore(store, logFn) {
322
+ const state = await store.get(getStorageKey());
323
+ if (state) {
324
+ logFn(`Found state: ${state.status}`);
325
+ return ok({ body: state });
326
+ }
327
+ logFn("No state found");
328
+ return noContent();
329
+ }
330
+ /**
284
331
  * Creates hooks to sync installation state to storage.
285
332
  */
286
333
  function createInstallationHooks(store, logFn) {
@@ -301,9 +348,14 @@ function createInstallationHooks(store, logFn) {
301
348
  * Installation action router.
302
349
  *
303
350
  * Routes:
304
- * - POST /installation - Start installation (creates plan, invokes execution async)
305
- * - GET /installation/execution - Get current execution status
306
- * - POST /installation/execution - Execute installation (internal, called async)
351
+ * - GET /installation - Get current installation status
352
+ * - POST /installation - Start installation (creates plan, invokes execution async)
353
+ * - POST /installation/execution - Execute installation (internal, called async)
354
+ * - POST /installation/validation - Pre-installation validation
355
+ * - POST /installation/uninstallation - Start uninstallation (async)
356
+ * - GET /installation/uninstallation - Get current uninstallation status
357
+ * - POST /installation/uninstallation/execution - Execute uninstallation (internal, called async)
358
+ * - DELETE /installation/uninstallation - Clear uninstallation state only (no offboarding)
307
359
  */
308
360
  const router = new HttpActionRouter().use(logger({ name: () => "installation" }));
309
361
  /**
@@ -316,13 +368,7 @@ const router = new HttpActionRouter().use(logger({ name: () => "installation" })
316
368
  */
317
369
  router.get("/", { handler: async (_req, { logger }) => {
318
370
  logger.debug("Getting installation execution status...");
319
- const state = await (await createInstallationStore()).get(getStorageKey());
320
- if (state) {
321
- logger.debug(`Found execution: ${state.status}`);
322
- return ok({ body: state });
323
- }
324
- logger.debug("No execution found");
325
- return noContent();
371
+ return readStateFromStore(await createInstallationStore(), (msg) => logger.debug(msg));
326
372
  } });
327
373
  /**
328
374
  * POST / - Start installation
@@ -354,17 +400,14 @@ router.post("/", {
354
400
  const initialState = createInitialInstallationState({ config: appConfig });
355
401
  logger.debug(`Created initial state: ${initialState.id}`);
356
402
  await store.put(getStorageKey(), initialState);
357
- const activation = await openwhisk().actions.invoke({
403
+ const ow = openwhisk();
404
+ const mergedParams = buildWorkflowParams(req.body, rawParams);
405
+ const activation = await ow.actions.invoke({
358
406
  name: DEFAULT_ACTION_NAME,
359
407
  blocking: false,
360
408
  result: false,
361
409
  params: {
362
- ...rawParams,
363
- appData: req.body.appData,
364
- AIO_EVENTS_API_BASE_URL: req.body.ioEventsUrl,
365
- AIO_COMMERCE_AUTH_IMS_ENVIRONMENT: req.body.ioEventsEnv,
366
- AIO_COMMERCE_API_BASE_URL: req.body.commerceBaseUrl,
367
- AIO_COMMERCE_API_FLAVOR: req.body.commerceEnv,
410
+ ...mergedParams,
368
411
  initialState,
369
412
  appConfig,
370
413
  __ow_path: "/execution",
@@ -386,18 +429,13 @@ router.post("/", {
386
429
  * It runs the actual installation workflow and saves state.
387
430
  */
388
431
  router.post("/execution", { handler: async (_req, { logger, rawParams }) => {
389
- const { appData, ...params } = rawParams;
432
+ const params = rawParams;
390
433
  const { initialState, appConfig } = params;
391
434
  if (!initialState) return badRequest("initialState is required for execution");
392
435
  if (!appConfig) return badRequest("appConfig is required for execution");
393
436
  const store = await createInstallationStore();
394
437
  const hooks = createInstallationHooks(store, (msg) => logger.debug(msg));
395
- const installationContext = {
396
- appData,
397
- params,
398
- logger,
399
- customScripts: params.customScriptsLoader?.(appConfig, logger) || {}
400
- };
438
+ const installationContext = buildInstallationContext(params, appConfig, logger);
401
439
  logger.debug(`Executing installation: ${initialState.id}`);
402
440
  const result = await runInstallation({
403
441
  installationContext,
@@ -432,14 +470,7 @@ router.post("/validation", {
432
470
  logger.debug("Running pre-installation validation...");
433
471
  const appConfig = rawParams.appConfig;
434
472
  if (!appConfig) return internalServerError("Could not find or parse the app.commerce.manifest.json file, is it present and valid?");
435
- const { appData, ...params } = {
436
- ...rawParams,
437
- appData: req.body.appData,
438
- AIO_EVENTS_API_BASE_URL: req.body.ioEventsUrl,
439
- AIO_COMMERCE_AUTH_IMS_ENVIRONMENT: req.body.ioEventsEnv,
440
- AIO_COMMERCE_API_BASE_URL: req.body.commerceBaseUrl,
441
- AIO_COMMERCE_API_FLAVOR: req.body.commerceEnv
442
- };
473
+ const { appData, ...params } = buildWorkflowParams(req.body, rawParams);
443
474
  const result = await runValidation({
444
475
  validationContext: {
445
476
  appData,
@@ -453,14 +484,107 @@ router.post("/validation", {
453
484
  }
454
485
  });
455
486
  /**
456
- * DELETE / - Clear installation state
487
+ * GET /uninstallation - Get current uninstallation status
488
+ *
489
+ * Returns 200 with state if an uninstallation has been started, 204 otherwise.
490
+ */
491
+ router.get("/uninstallation", { handler: async (_req, { logger }) => {
492
+ logger.debug("Getting uninstallation execution status...");
493
+ return readStateFromStore(await createUninstallationStore(), (msg) => logger.debug(msg));
494
+ } });
495
+ /**
496
+ * POST /uninstallation - Start uninstallation (async)
497
+ *
498
+ * Flow:
499
+ * 1. Check uninstallation store for existing state
500
+ * 2. If in-progress: return 409 Conflict
501
+ * 3. Create initial uninstall state, save to store
502
+ * 4. Invoke POST /uninstallation/execution async via openwhisk
503
+ * 5. Return 202 Accepted with initial state
504
+ */
505
+ router.post("/uninstallation", {
506
+ body: InstallationRequestBodySchema,
507
+ handler: async (req, { logger, rawParams }) => {
508
+ logger.debug("Starting async uninstallation...");
509
+ const appConfig = rawParams.appConfig;
510
+ if (!appConfig) return internalServerError("Could not find or parse the app.commerce.manifest.json file, is it present and valid?");
511
+ const store = await createUninstallationStore();
512
+ const existingState = await store.get(getStorageKey());
513
+ if (existingState && isInProgressState(existingState)) {
514
+ logger.debug(`Uninstallation already in progress: ${existingState.status}`);
515
+ return conflict("Uninstallation is already in progress. Wait for it to complete.");
516
+ }
517
+ const initialState = createInitialUninstallationState({ config: appConfig });
518
+ logger.debug(`Created initial uninstall state: ${initialState.id}`);
519
+ await store.put(getStorageKey(), initialState);
520
+ const workflowParams = buildWorkflowParams(req.body, rawParams);
521
+ const activation = await openwhisk().actions.invoke({
522
+ name: DEFAULT_ACTION_NAME,
523
+ blocking: false,
524
+ result: false,
525
+ params: {
526
+ ...workflowParams,
527
+ initialState,
528
+ appConfig,
529
+ __ow_path: "/uninstallation/execution",
530
+ __ow_method: "post"
531
+ }
532
+ });
533
+ logger.debug(`Async uninstallation started: ${activation.activationId}`);
534
+ return accepted({ body: {
535
+ message: "Uninstallation started",
536
+ activationId: activation.activationId,
537
+ ...initialState
538
+ } });
539
+ }
540
+ });
541
+ /**
542
+ * POST /uninstallation/execution - Execute uninstallation (internal, called async by POST /uninstallation)
543
+ *
544
+ * Flow:
545
+ * 1. Build InstallationContext from params
546
+ * 2. Run uninstallation workflow with hooks (hooks persist state per step)
547
+ * 3. Save final state to uninstallation store
548
+ * 4. On success, clear installation store
549
+ * 5. Return 200 on success, 500 on failure
550
+ */
551
+ router.post("/uninstallation/execution", { handler: async (_req, { logger, rawParams }) => {
552
+ const params = rawParams;
553
+ const { initialState, appConfig } = params;
554
+ if (!initialState) return badRequest("initialState is required for execution");
555
+ if (!appConfig) return badRequest("appConfig is required for execution");
556
+ const store = await createUninstallationStore();
557
+ const hooks = createInstallationHooks(store, (msg) => logger.debug(msg));
558
+ const installationContext = buildInstallationContext(params, appConfig, logger);
559
+ logger.debug(`Executing uninstallation: ${initialState.id}`);
560
+ const result = await runUninstallation({
561
+ installationContext,
562
+ config: appConfig,
563
+ initialState,
564
+ hooks
565
+ });
566
+ await store.put(getStorageKey(), result);
567
+ logger.debug(`Uninstallation completed: ${result.status}`);
568
+ if (isSucceededState(result)) {
569
+ await (await createInstallationStore()).delete(getStorageKey());
570
+ logger.debug("Cleared installation state after successful uninstallation");
571
+ }
572
+ if (isFailedState(result)) return internalServerError({ body: {
573
+ message: "Uninstallation failed",
574
+ error: result.error,
575
+ state: result
576
+ } });
577
+ return ok({ body: result });
578
+ } });
579
+ /**
580
+ * DELETE /uninstallation - Clear uninstallation state
457
581
  *
458
- * This endpoint allows clearing the installation state.
582
+ * Removes the stored uninstallation state without triggering any offboarding.
459
583
  */
460
- router.delete("/", { handler: async (_req, { logger }) => {
461
- logger.debug("Clearing installation state...");
462
- await (await createInstallationStore()).delete(getStorageKey());
463
- logger.debug("Installation state cleared");
584
+ router.delete("/uninstallation", { handler: async (_req, { logger }) => {
585
+ logger.debug("Clearing uninstallation state...");
586
+ await (await createUninstallationStore()).delete(getStorageKey());
587
+ logger.debug("Uninstallation state cleared");
464
588
  return noContent();
465
589
  } });
466
590
  /** Factory to create the route handler for the `installation` action. */
@@ -62,32 +62,43 @@ type StepContextFactory<TStepCtx extends Record<string, unknown> = Record<string
62
62
  type ExecutionContext<TStepCtx extends Record<string, unknown> = Record<string, unknown>> = InstallationContext & TStepCtx;
63
63
  /**
64
64
  * A narrowed context available to step `validate` handlers.
65
- * Excludes `customScripts` — those only apply during installation `run`, not pre-flight validation.
65
+ * Excludes `customScripts` — those only apply during installation, not pre-flight validation.
66
66
  */
67
67
  type ValidationContext = Omit<InstallationContext, "customScripts">;
68
68
  /** The context passed to step `validate` handlers (base validation context merged with step-level context). */
69
69
  type ValidationExecutionContext<TStepCtx extends Record<string, unknown> = Record<string, unknown>> = ValidationContext & TStepCtx;
70
- /** Metadata for a step (used for UI display). */
71
- type StepMeta = {
70
+ /** Metadata info for a step (used for UI display). */
71
+ type StepMetaInfo = {
72
72
  label: string;
73
73
  description?: string;
74
74
  };
75
+ /** Step metadata keyed by execution mode. */
76
+ type StepMeta = {
77
+ install: StepMetaInfo;
78
+ uninstall?: StepMetaInfo;
79
+ };
75
80
  /** Defines the base properties of a step. */
76
81
  type StepBase<TName extends string = string, TConfig extends CommerceAppConfigOutputModel = CommerceAppConfigOutputModel> = {
77
- /** The name of this step. */name: TName; /** Metadata associated with the step. */
82
+ /** The name of this step. */name: TName; /** Metadata associated with the step, keyed by execution mode. */
78
83
  meta: StepMeta; /** Whether the step should be taken into consideration. */
79
84
  when?: (config: CommerceAppConfigOutputModel) => config is TConfig;
80
85
  };
81
86
  /** A leaf step that executes work (no children). */
82
87
  type LeafStep<TName extends string = string, TConfig extends CommerceAppConfigOutputModel = CommerceAppConfigOutputModel, TStepCtx extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown> = StepBase<TName, TConfig> & {
83
88
  type: "leaf"; /** The execution handler for the step. */
84
- run: (config: TConfig, context: ExecutionContext<TStepCtx>) => TOutput | Promise<TOutput>;
89
+ install: (config: TConfig, context: ExecutionContext<TStepCtx>) => TOutput | Promise<TOutput>;
85
90
  /**
86
91
  * Optional pre-installation validation handler.
87
92
  * Called before installation begins to surface issues (errors or warnings).
88
93
  * Returning an empty array means the step has no issues.
89
94
  */
90
95
  validate?: (config: TConfig, context: ValidationExecutionContext<TStepCtx>) => ValidationIssue[] | Promise<ValidationIssue[]>;
96
+ /**
97
+ * Optional uninstall handler for the step.
98
+ * Called during uninstallation to reverse the work done by `install`.
99
+ * If absent, the step is silently skipped during uninstallation.
100
+ */
101
+ uninstall?: (config: TConfig, context: ExecutionContext<TStepCtx>) => void | Promise<void>;
91
102
  };
92
103
  /** A branch step that contains children (no execution). */
93
104
  type BranchStep<TName extends string = string, TConfig extends CommerceAppConfigOutputModel = CommerceAppConfigOutputModel, TStepCtx extends Record<string, unknown> = Record<string, unknown>, TChildren extends AnyStep[] = AnyStep[]> = StepBase<TName, TConfig> & {
@@ -107,10 +118,11 @@ type Step<TName extends string = string, TConfig extends CommerceAppConfigOutput
107
118
  interface AnyStep {
108
119
  children?: AnyStep[];
109
120
  context?: (context: InstallationContext) => any;
121
+ install?: (config: any, context: any) => unknown | Promise<unknown>;
110
122
  meta: StepMeta;
111
123
  name: string;
112
- run?: (config: any, context: any) => unknown | Promise<unknown>;
113
124
  type: "leaf" | "branch";
125
+ uninstall?: (config: any, context: any) => void | Promise<void>;
114
126
  validate?: (config: any, context: any) => ValidationIssue[] | Promise<ValidationIssue[]>;
115
127
  when?: (config: CommerceAppConfigOutputModel) => boolean;
116
128
  }
@@ -129,8 +141,8 @@ type BranchStepOptions<TName extends string, TConfig extends CommerceAppConfigOu
129
141
  * ```typescript
130
142
  * const createProviders = defineLeafStep({
131
143
  * name: "providers",
132
- * meta: { label: "Create Providers", description: "Creates I/O Events providers" },
133
- * run: async ({ config, stepContext }) => {
144
+ * meta: { install: { label: "Create Providers", description: "Creates I/O Events providers" } },
145
+ * install: async ({ config, stepContext }) => {
134
146
  * const { eventsClient } = stepContext;
135
147
  * return eventsClient.createProvider(config.eventing);
136
148
  * },
@@ -142,7 +154,8 @@ declare function defineLeafStep<TName extends string, TConfig extends CommerceAp
142
154
  name: TName;
143
155
  meta: StepMeta;
144
156
  when: ((config: CommerceAppConfigOutputModel) => config is TConfig) | undefined;
145
- run: (config: TConfig, context: ExecutionContext<TStepCtx>) => TOutput | Promise<TOutput>;
157
+ install: (config: TConfig, context: ExecutionContext<TStepCtx>) => TOutput | Promise<TOutput>;
158
+ uninstall: ((config: TConfig, context: ExecutionContext<TStepCtx>) => void | Promise<void>) | undefined;
146
159
  validate: ((config: TConfig, context: ValidationExecutionContext<TStepCtx>) => ValidationIssue[] | Promise<ValidationIssue[]>) | undefined;
147
160
  };
148
161
  /**
@@ -152,7 +165,7 @@ declare function defineLeafStep<TName extends string, TConfig extends CommerceAp
152
165
  * ```typescript
153
166
  * const eventing = defineBranchStep({
154
167
  * name: "eventing",
155
- * meta: { label: "Eventing", description: "Sets up I/O Events" },
168
+ * meta: { install: { label: "Eventing", description: "Sets up I/O Events" } },
156
169
  * when: hasEventing,
157
170
  * context: async (ctx) => ({ eventsClient: await createEventsClient(ctx) }),
158
171
  * children: [commerceEventsStep, externalEventsStep],
@@ -188,7 +201,7 @@ type StepStatus = {
188
201
  /** Step name (unique among siblings). */name: string; /** Unique step identifier (e.g., UUID). */
189
202
  id: string; /** Full path from root to this step. */
190
203
  path: string[]; /** Step metadata (for display purposes). */
191
- meta: StepMeta; /** Current execution status. */
204
+ meta: StepMetaInfo; /** Current execution status. */
192
205
  status: ExecutionStatus; /** Child step statuses (empty for leaf steps). */
193
206
  children: StepStatus[];
194
207
  };
@@ -270,7 +283,8 @@ type InstallationHooks = {
270
283
  /** Options for creating an initial installation state. */
271
284
  type CreateInitialStateOptions = {
272
285
  /** The root branch step to build the state from. */rootStep: BranchStep; /** The app configuration used to determine applicable steps. */
273
- config: CommerceAppConfigOutputModel;
286
+ config: CommerceAppConfigOutputModel; /** The execution mode. When "uninstall", steps use `meta.uninstall` if defined; defaults to "install". */
287
+ mode?: ExecutionMode;
274
288
  };
275
289
  /** Options for executing a workflow. */
276
290
  type ExecuteWorkflowOptions = {
@@ -280,6 +294,8 @@ type ExecuteWorkflowOptions = {
280
294
  initialState: InProgressInstallationState; /** Lifecycle hooks for status change notifications. */
281
295
  hooks?: InstallationHooks;
282
296
  };
297
+ /** Execution mode: "install" or "uninstall". */
298
+ type ExecutionMode = "install" | "uninstall";
283
299
  /**
284
300
  * Creates an initial installation state from a root step and config.
285
301
  *
@@ -291,13 +307,18 @@ declare function createInitialState(options: CreateInitialStateOptions): InProgr
291
307
  * Executes a workflow from an initial state. Returns the final state (never throws).
292
308
  */
293
309
  declare function executeWorkflow(options: ExecuteWorkflowOptions): Promise<SucceededInstallationState | FailedInstallationState>;
310
+ /**
311
+ * Executes an uninstall workflow from an initial state. Returns the final state (never throws).
312
+ * Steps with an `uninstall` handler get it called; steps without are silently skipped.
313
+ */
314
+ declare function executeUninstallWorkflow(options: ExecuteWorkflowOptions): Promise<SucceededInstallationState | FailedInstallationState>;
294
315
  //#endregion
295
316
  //#region source/management/installation/workflow/validation.d.ts
296
317
  /** Validation result for a single step, mirroring the step hierarchy. */
297
318
  type StepValidationResult = {
298
319
  /** Step name (unique among siblings). */name: string; /** Full path from root to this step. */
299
320
  path: string[]; /** Step metadata (for display purposes). */
300
- meta: StepMeta; /** Issues found for this specific step (not including children). */
321
+ meta: StepMetaInfo; /** Issues found for this specific step (not including children). */
301
322
  issues: ValidationIssue[]; /** Validation results for child steps (empty for leaf steps). */
302
323
  children: StepValidationResult[];
303
324
  };
@@ -342,35 +363,49 @@ declare function validateStepTree(options: ValidateStepTreeOptions): Promise<Val
342
363
  * @returns The result of the installation step (can be any value or Promise)
343
364
  */
344
365
  type CustomInstallationStepHandler<TResult = unknown> = (config: CommerceAppConfigOutputModel, context: ExecutionContext) => TResult | Promise<TResult>;
366
+ /**
367
+ * Object form for defining a custom installation step with both install and uninstall handlers.
368
+ *
369
+ * @template TResult - The return type of the install handler
370
+ */
371
+ type CustomInstallationStepDefinition<TResult = unknown> = {
372
+ /** The installation handler, called when the app is installed. */install: CustomInstallationStepHandler<TResult>; /** The optional uninstall handler, called when the app is uninstalled. */
373
+ uninstall?: CustomInstallationStepHandler<void>;
374
+ };
345
375
  /**
346
376
  * Define a custom installation step with type-safe parameters.
347
377
  *
348
378
  * This helper provides type safety and IDE autocompletion for custom installation scripts.
349
- * The handler function receives properly typed `config` and `context` parameters.
379
+ * Accepts either a plain function (install only) or an object with `install` and optional
380
+ * `uninstall` handlers.
350
381
  *
351
- * @param handler - The installation step handler function
352
- * @returns The same handler function (for use as default export)
353
- *
354
- * @example
382
+ * @example Plain function (install only):
355
383
  * ```typescript
356
384
  * import { defineCustomInstallationStep } from "@adobe/aio-commerce-lib-app/management";
357
385
  *
358
386
  * export default defineCustomInstallationStep(async (config, context) => {
359
387
  * const { logger, params } = context;
360
- *
361
388
  * logger.info(`Setting up ${config.metadata.displayName}...`);
389
+ * return { status: "success" };
390
+ * });
391
+ * ```
362
392
  *
363
- * // Your installation logic here
364
- * // TypeScript will provide autocompletion for config and context
393
+ * @example Object form with install and uninstall:
394
+ * ```typescript
395
+ * import { defineCustomInstallationStep } from "@adobe/aio-commerce-lib-app/management";
365
396
  *
366
- * return {
367
- * status: "success",
368
- * message: "Setup completed",
369
- * };
397
+ * export default defineCustomInstallationStep({
398
+ * install: async (config, context) => {
399
+ * context.logger.info(`Registering ${config.metadata.displayName}...`);
400
+ * return { status: "success" };
401
+ * },
402
+ * uninstall: async (config, context) => {
403
+ * context.logger.info(`Removing ${config.metadata.displayName}...`);
404
+ * },
370
405
  * });
371
406
  * ```
372
407
  */
373
- declare function defineCustomInstallationStep<TResult = unknown>(handler: CustomInstallationStepHandler<TResult>): CustomInstallationStepHandler<TResult>;
408
+ declare function defineCustomInstallationStep<TResult = unknown>(handlerOrDefinition: CustomInstallationStepHandler<TResult> | CustomInstallationStepDefinition<TResult>): CustomInstallationStepHandler<TResult> | CustomInstallationStepDefinition<TResult>;
374
409
  //#endregion
375
410
  //#region source/management/installation/runner.d.ts
376
411
  /** Options for creating an initial installation state. */
@@ -394,6 +429,25 @@ declare function createInitialInstallationState(options: CreateInitialInstallati
394
429
  * Runs the full installation workflow. Returns the final state (never throws).
395
430
  */
396
431
  declare function runInstallation(options: RunInstallationOptions): Promise<SucceededInstallationState | FailedInstallationState>;
432
+ /** Options for creating an initial uninstallation state. */
433
+ type CreateInitialUninstallationStateOptions = {
434
+ /** The app configuration used to determine applicable steps. */config: CommerceAppConfigOutputModel;
435
+ };
436
+ /** Options for running an uninstallation. */
437
+ type RunUninstallationOptions = {
438
+ /** Shared installation context (params, logger, etc.). */installationContext: InstallationContext; /** The app configuration. */
439
+ config: CommerceAppConfigOutputModel; /** The initial uninstallation state (with all steps pending). */
440
+ initialState: InProgressInstallationState; /** Lifecycle hooks for status change notifications. */
441
+ hooks?: InstallationHooks;
442
+ };
443
+ /**
444
+ * Creates an initial uninstallation state from the config and step definitions.
445
+ */
446
+ declare function createInitialUninstallationState(options: CreateInitialUninstallationStateOptions): InProgressInstallationState;
447
+ /**
448
+ * Runs the full uninstallation workflow. Returns the final state (never throws).
449
+ */
450
+ declare function runUninstallation(options: RunUninstallationOptions): Promise<SucceededInstallationState | FailedInstallationState>;
397
451
  /** Options for running pre-installation validation. */
398
452
  type RunValidationOptions = {
399
453
  /** Validation context (params, logger, appData — no customScripts). */validationContext: ValidationContext; /** The app configuration. */
@@ -409,4 +463,4 @@ type RunValidationOptions = {
409
463
  */
410
464
  declare function runValidation(options: RunValidationOptions): Promise<ValidationResult>;
411
465
  //#endregion
412
- export { isBranchStep as $, StepStatus as A, InferStepOutput as B, ExecutionStatus as C, InstallationError as D, InstallationData as E, isSucceededState as F, StepContextFactory as G, LeafStep as H, AnyStep as I, ValidationExecutionContext as J, StepMeta as K, BranchStep as L, isCompletedState as M, isFailedState as N, InstallationState as O, isInProgressState as P, defineLeafStep as Q, BranchStepOptions as R, StepSucceededEvent as S, InProgressInstallationState as T, LeafStepOptions as U, InstallationContext as V, Step as W, ValidationIssueSeverity as X, ValidationIssue as Y, defineBranchStep as Z, executeWorkflow as _, runInstallation as a, StepFailedEvent as b, defineCustomInstallationStep as c, ValidationResult as d, isLeafStep as et, ValidationSummary as f, createInitialState as g, ExecuteWorkflowOptions as h, createInitialInstallationState as i, SucceededInstallationState as j, InstallationStatus as k, StepValidationResult as l, CreateInitialStateOptions as m, RunInstallationOptions as n, runValidation as o, validateStepTree as p, ValidationContext as q, RunValidationOptions as r, CustomInstallationStepHandler as s, CreateInitialInstallationStateOptions as t, ValidateStepTreeOptions as u, InstallationHooks as v, FailedInstallationState as w, StepStartedEvent as x, StepEvent as y, ExecutionContext as z };
466
+ export { StepMetaInfo as $, FailedInstallationState as A, isInProgressState as B, executeWorkflow as C, StepStartedEvent as D, StepFailedEvent as E, InstallationStatus as F, ExecutionContext as G, AnyStep as H, StepStatus as I, LeafStep as J, InferStepOutput as K, SucceededInstallationState as L, InstallationData as M, InstallationError as N, StepSucceededEvent as O, InstallationState as P, StepMeta as Q, isCompletedState as R, executeUninstallWorkflow as S, StepEvent as T, BranchStep as U, isSucceededState as V, BranchStepOptions as W, Step as X, LeafStepOptions as Y, StepContextFactory as Z, ValidationSummary as _, RunValidationOptions as a, defineLeafStep as at, ExecuteWorkflowOptions as b, runInstallation as c, CustomInstallationStepDefinition as d, ValidationContext as et, CustomInstallationStepHandler as f, ValidationResult as g, ValidateStepTreeOptions as h, RunUninstallationOptions as i, defineBranchStep as it, InProgressInstallationState as j, ExecutionStatus as k, runUninstallation as l, StepValidationResult as m, CreateInitialUninstallationStateOptions as n, ValidationIssue as nt, createInitialInstallationState as o, isBranchStep as ot, defineCustomInstallationStep as p, InstallationContext as q, RunInstallationOptions as r, ValidationIssueSeverity as rt, createInitialUninstallationState as s, isLeafStep as st, CreateInitialInstallationStateOptions as t, ValidationExecutionContext as tt, runValidation as u, validateStepTree as v, InstallationHooks as w, createInitialState as x, CreateInitialStateOptions as y, isFailedState as z };
@@ -12,5 +12,5 @@
12
12
  * governing permissions and limitations under the License.
13
13
  */
14
14
 
15
- import { $ as isBranchStep, A as StepStatus, B as InferStepOutput, C as ExecutionStatus, D as InstallationError, E as InstallationData, F as isSucceededState, G as StepContextFactory, H as LeafStep, I as AnyStep, J as ValidationExecutionContext, K as StepMeta, L as BranchStep, M as isCompletedState, N as isFailedState, O as InstallationState, P as isInProgressState, Q as defineLeafStep, R as BranchStepOptions, S as StepSucceededEvent, T as InProgressInstallationState, U as LeafStepOptions, V as InstallationContext, W as Step, X as ValidationIssueSeverity, Y as ValidationIssue, Z as defineBranchStep, _ as executeWorkflow, a as runInstallation, b as StepFailedEvent, c as defineCustomInstallationStep, d as ValidationResult, et as isLeafStep, f as ValidationSummary, g as createInitialState, h as ExecuteWorkflowOptions, i as createInitialInstallationState, j as SucceededInstallationState, k as InstallationStatus, l as StepValidationResult, m as CreateInitialStateOptions, n as RunInstallationOptions, o as runValidation, p as validateStepTree, q as ValidationContext, r as RunValidationOptions, s as CustomInstallationStepHandler, t as CreateInitialInstallationStateOptions, u as ValidateStepTreeOptions, v as InstallationHooks, w as FailedInstallationState, x as StepStartedEvent, y as StepEvent, z as ExecutionContext } from "../index-BmYXe7kp.mjs";
16
- export { AnyStep, BranchStep, BranchStepOptions, CreateInitialInstallationStateOptions, CreateInitialStateOptions, CustomInstallationStepHandler, ExecuteWorkflowOptions, ExecutionContext, ExecutionStatus, FailedInstallationState, InProgressInstallationState, InferStepOutput, InstallationContext, InstallationData, InstallationError, InstallationHooks, InstallationState, InstallationStatus, LeafStep, LeafStepOptions, RunInstallationOptions, RunValidationOptions, Step, StepContextFactory, StepEvent, StepFailedEvent, StepMeta, StepStartedEvent, StepStatus, StepSucceededEvent, StepValidationResult, SucceededInstallationState, ValidateStepTreeOptions, ValidationContext, ValidationExecutionContext, ValidationIssue, ValidationIssueSeverity, ValidationResult, ValidationSummary, createInitialInstallationState, createInitialState, defineBranchStep, defineCustomInstallationStep, defineLeafStep, executeWorkflow, isBranchStep, isCompletedState, isFailedState, isInProgressState, isLeafStep, isSucceededState, runInstallation, runValidation, validateStepTree };
15
+ import { $ as StepMetaInfo, A as FailedInstallationState, B as isInProgressState, C as executeWorkflow, D as StepStartedEvent, E as StepFailedEvent, F as InstallationStatus, G as ExecutionContext, H as AnyStep, I as StepStatus, J as LeafStep, K as InferStepOutput, L as SucceededInstallationState, M as InstallationData, N as InstallationError, O as StepSucceededEvent, P as InstallationState, Q as StepMeta, R as isCompletedState, S as executeUninstallWorkflow, T as StepEvent, U as BranchStep, V as isSucceededState, W as BranchStepOptions, X as Step, Y as LeafStepOptions, Z as StepContextFactory, _ as ValidationSummary, a as RunValidationOptions, at as defineLeafStep, b as ExecuteWorkflowOptions, c as runInstallation, d as CustomInstallationStepDefinition, et as ValidationContext, f as CustomInstallationStepHandler, g as ValidationResult, h as ValidateStepTreeOptions, i as RunUninstallationOptions, it as defineBranchStep, j as InProgressInstallationState, k as ExecutionStatus, l as runUninstallation, m as StepValidationResult, n as CreateInitialUninstallationStateOptions, nt as ValidationIssue, o as createInitialInstallationState, ot as isBranchStep, p as defineCustomInstallationStep, q as InstallationContext, r as RunInstallationOptions, rt as ValidationIssueSeverity, s as createInitialUninstallationState, st as isLeafStep, t as CreateInitialInstallationStateOptions, tt as ValidationExecutionContext, u as runValidation, v as validateStepTree, w as InstallationHooks, x as createInitialState, y as CreateInitialStateOptions, z as isFailedState } from "../index-BENO5T7n.mjs";
16
+ export { AnyStep, BranchStep, BranchStepOptions, CreateInitialInstallationStateOptions, CreateInitialStateOptions, CreateInitialUninstallationStateOptions, CustomInstallationStepDefinition, CustomInstallationStepHandler, ExecuteWorkflowOptions, ExecutionContext, ExecutionStatus, FailedInstallationState, InProgressInstallationState, InferStepOutput, InstallationContext, InstallationData, InstallationError, InstallationHooks, InstallationState, InstallationStatus, LeafStep, LeafStepOptions, RunInstallationOptions, RunUninstallationOptions, RunValidationOptions, Step, StepContextFactory, StepEvent, StepFailedEvent, StepMeta, StepMetaInfo, StepStartedEvent, StepStatus, StepSucceededEvent, StepValidationResult, SucceededInstallationState, ValidateStepTreeOptions, ValidationContext, ValidationExecutionContext, ValidationIssue, ValidationIssueSeverity, ValidationResult, ValidationSummary, createInitialInstallationState, createInitialState, createInitialUninstallationState, defineBranchStep, defineCustomInstallationStep, defineLeafStep, executeUninstallWorkflow, executeWorkflow, isBranchStep, isCompletedState, isFailedState, isInProgressState, isLeafStep, isSucceededState, runInstallation, runUninstallation, runValidation, validateStepTree };
@@ -12,6 +12,6 @@
12
12
  * governing permissions and limitations under the License.
13
13
  */
14
14
 
15
- import { a as isCompletedState, c as isSucceededState, i as defineCustomInstallationStep, n as runInstallation, o as isFailedState, r as runValidation, s as isInProgressState, t as createInitialInstallationState } from "../management-DSexEPTW.mjs";
15
+ import { a as runValidation, c as isFailedState, i as runUninstallation, l as isInProgressState, n as createInitialUninstallationState, o as defineCustomInstallationStep, r as runInstallation, s as isCompletedState, t as createInitialInstallationState, u as isSucceededState } from "../management-ByHvVJ12.mjs";
16
16
 
17
- export { createInitialInstallationState, defineCustomInstallationStep, isCompletedState, isFailedState, isInProgressState, isSucceededState, runInstallation, runValidation };
17
+ export { createInitialInstallationState, createInitialUninstallationState, defineCustomInstallationStep, isCompletedState, isFailedState, isInProgressState, isSucceededState, runInstallation, runUninstallation, runValidation };