@adobe/aio-commerce-lib-app 0.1.0 → 0.2.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 (37) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/cjs/actions/index.cjs +179 -0
  3. package/dist/cjs/actions/index.d.cts +18 -0
  4. package/dist/cjs/app-C4HhkXbP.d.cts +451 -0
  5. package/dist/cjs/commands/generate/actions/templates/custom-scripts.js.template +28 -0
  6. package/dist/cjs/commands/generate/actions/templates/get-app-config.js.template +11 -10
  7. package/dist/cjs/commands/generate/actions/templates/installation.js.template +21 -0
  8. package/dist/cjs/commands/index.cjs +39 -50
  9. package/dist/cjs/config/index.cjs +1 -14
  10. package/dist/cjs/config/index.d.cts +107 -423
  11. package/dist/cjs/error-yAk1zzvx.cjs +1 -0
  12. package/dist/cjs/index-DS3IlISO.d.cts +345 -0
  13. package/dist/cjs/management/index.cjs +1 -0
  14. package/dist/cjs/management/index.d.cts +2 -0
  15. package/dist/cjs/management-CE3_DJw4.cjs +2 -0
  16. package/dist/cjs/parser-Dovux8ce.cjs +1 -0
  17. package/dist/cjs/schemas-CdaP-Exw.cjs +1 -0
  18. package/dist/es/actions/index.d.mts +18 -0
  19. package/dist/es/actions/index.mjs +179 -0
  20. package/dist/es/app-CMpx3D7Y.d.mts +451 -0
  21. package/dist/es/chunk-VmiN0kV1.mjs +1 -0
  22. package/dist/es/commands/generate/actions/templates/custom-scripts.js.template +28 -0
  23. package/dist/es/commands/generate/actions/templates/get-app-config.js.template +11 -10
  24. package/dist/es/commands/generate/actions/templates/installation.js.template +21 -0
  25. package/dist/es/commands/index.mjs +39 -50
  26. package/dist/es/config/index.d.mts +107 -423
  27. package/dist/es/config/index.mjs +1 -14
  28. package/dist/es/error-hBHRgZ9R.mjs +1 -0
  29. package/dist/es/index-DQepSWYP.d.mts +345 -0
  30. package/dist/es/management/index.d.mts +3 -0
  31. package/dist/es/management/index.mjs +1 -0
  32. package/dist/es/management-BM2WcbV6.mjs +2 -0
  33. package/dist/es/parser-DOVfvr9l.mjs +1 -0
  34. package/dist/es/schemas-eemlD-xS.mjs +1 -0
  35. package/package.json +34 -6
  36. package/dist/cjs/parser-DsI4Y-Z2.cjs +0 -14
  37. package/dist/es/parser-BoZ6cYn-.mjs +0 -14
@@ -0,0 +1,345 @@
1
+ import { r as CommerceAppConfigOutputModel } from "./app-CMpx3D7Y.mjs";
2
+ import * as v from "valibot";
3
+ import { RuntimeActionParams } from "@adobe/aio-commerce-lib-core/params";
4
+ import AioLogger from "@adobe/aio-lib-core-logging";
5
+
6
+ //#region source/management/installation/schema.d.ts
7
+ /** Schema for validating Adobe I/O app credentials required for installation. */
8
+ declare const AppDataSchema: v.ObjectSchema<{
9
+ readonly consumerOrgId: v.SchemaWithPipe<readonly [v.StringSchema<`Expected a string value for '${string}'`>, v.NonEmptyAction<string, `The value of "${string}" must not be empty`>]>;
10
+ readonly orgName: v.SchemaWithPipe<readonly [v.StringSchema<`Expected a string value for '${string}'`>, v.NonEmptyAction<string, `The value of "${string}" must not be empty`>]>;
11
+ readonly projectId: v.SchemaWithPipe<readonly [v.StringSchema<`Expected a string value for '${string}'`>, v.NonEmptyAction<string, `The value of "${string}" must not be empty`>]>;
12
+ readonly projectName: v.SchemaWithPipe<readonly [v.StringSchema<`Expected a string value for '${string}'`>, v.NonEmptyAction<string, `The value of "${string}" must not be empty`>]>;
13
+ readonly projectTitle: v.SchemaWithPipe<readonly [v.StringSchema<`Expected a string value for '${string}'`>, v.NonEmptyAction<string, `The value of "${string}" must not be empty`>]>;
14
+ readonly workspaceId: v.SchemaWithPipe<readonly [v.StringSchema<`Expected a string value for '${string}'`>, v.NonEmptyAction<string, `The value of "${string}" must not be empty`>]>;
15
+ readonly workspaceName: v.SchemaWithPipe<readonly [v.StringSchema<`Expected a string value for '${string}'`>, v.NonEmptyAction<string, `The value of "${string}" must not be empty`>]>;
16
+ readonly workspaceTitle: v.SchemaWithPipe<readonly [v.StringSchema<`Expected a string value for '${string}'`>, v.NonEmptyAction<string, `The value of "${string}" must not be empty`>]>;
17
+ }, undefined>;
18
+ /** Type for Adobe I/O app credentials. */
19
+ type AppData = v.InferOutput<typeof AppDataSchema>;
20
+ //#endregion
21
+ //#region source/management/installation/workflow/step.d.ts
22
+ /** Shared context available to all steps during installation. */
23
+ type InstallationContext = {
24
+ /** The credentials of the app being installed */
25
+ appData: AppData;
26
+ /** The raw action parameters from the App Builder runtime action. */
27
+ params: RuntimeActionParams & {
28
+ AIO_COMMERCE_AUTH_IMS_CLIENT_ID: string;
29
+ AIO_COMMERCE_AUTH_IMS_CLIENT_SECRETS: string | string[];
30
+ AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_ID: string;
31
+ AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_EMAIL: string;
32
+ AIO_COMMERCE_AUTH_IMS_ORG_ID: string;
33
+ AIO_COMMERCE_AUTH_IMS_SCOPES: string | string[];
34
+ };
35
+ /** Logger instance for installation logging. */
36
+ logger: ReturnType<typeof AioLogger>;
37
+ /** Custom scripts defined in the configuration (if any). */
38
+ customScripts?: Record<string, unknown>;
39
+ };
40
+ /** Factory function type for creating step-specific context. */
41
+ type StepContextFactory<TStepCtx extends Record<string, unknown> = Record<string, unknown>> = (context: InstallationContext) => TStepCtx | Promise<TStepCtx>;
42
+ /** The execution context passed to leaf step run handlers. */
43
+ type ExecutionContext<TStepCtx extends Record<string, unknown> = Record<string, unknown>> = InstallationContext & TStepCtx;
44
+ /** Metadata for a step (used for UI display). */
45
+ type StepMeta = {
46
+ label: string;
47
+ description?: string;
48
+ };
49
+ /** Defines the base properties of a step. */
50
+ type StepBase<TName extends string = string, TConfig extends CommerceAppConfigOutputModel = CommerceAppConfigOutputModel> = {
51
+ /** The name of this step. */
52
+ name: TName;
53
+ /** Metadata associated with the step. */
54
+ meta: StepMeta;
55
+ /** Whether the step should be taken into consideration. */
56
+ when?: (config: CommerceAppConfigOutputModel) => config is TConfig;
57
+ };
58
+ /** A leaf step that executes work (no children). */
59
+ type LeafStep<TName extends string = string, TConfig extends CommerceAppConfigOutputModel = CommerceAppConfigOutputModel, TStepCtx extends Record<string, unknown> = Record<string, unknown>, TOutput$1 = unknown> = StepBase<TName, TConfig> & {
60
+ type: "leaf";
61
+ /** The execution handler for the step. */
62
+ run: (config: TConfig, context: ExecutionContext<TStepCtx>) => TOutput$1 | Promise<TOutput$1>;
63
+ };
64
+ /** A branch step that contains children (no execution). */
65
+ type BranchStep<TName extends string = string, TConfig extends CommerceAppConfigOutputModel = CommerceAppConfigOutputModel, TStepCtx extends Record<string, unknown> = Record<string, unknown>> = StepBase<TName, TConfig> & {
66
+ type: "branch";
67
+ /** An optional factory function to setup shared context for the children steps. */
68
+ context?: StepContextFactory<TStepCtx>;
69
+ /** The children steps of this branch. */
70
+ children: AnyStep[];
71
+ };
72
+ /** A step in the installation tree (discriminated union by `type`). */
73
+ type Step<TName extends string = string, TConfig extends CommerceAppConfigOutputModel = CommerceAppConfigOutputModel, TStepCtx extends Record<string, unknown> = Record<string, unknown>, TOutput$1 = unknown> = LeafStep<TName, TConfig, TStepCtx, TOutput$1> | BranchStep<TName, TConfig, TStepCtx>;
74
+ /** Loosely-typed step for use in non type-safe contexts. */
75
+ interface AnyStep {
76
+ type: "leaf" | "branch";
77
+ name: string;
78
+ meta: StepMeta;
79
+ when?: (config: CommerceAppConfigOutputModel) => boolean;
80
+ children?: AnyStep[];
81
+ context?: (context: InstallationContext) => any;
82
+ run?: (config: any, context: any) => unknown | Promise<unknown>;
83
+ }
84
+ /** Check if a step is a leaf step. */
85
+ declare function isLeafStep(step: AnyStep): step is LeafStep;
86
+ /** Check if a step is a branch step. */
87
+ declare function isBranchStep(step: AnyStep): step is BranchStep;
88
+ /** Options for defining a leaf step. */
89
+ type LeafStepOptions<TName extends string, TConfig extends CommerceAppConfigOutputModel, TStepCtx extends Record<string, unknown> = Record<string, unknown>, TOutput$1 = unknown> = Omit<LeafStep<TName, TConfig, TStepCtx, TOutput$1>, "type">;
90
+ /** Options for defining a branch step. */
91
+ type BranchStepOptions<TName extends string, TConfig extends CommerceAppConfigOutputModel, TStepCtx extends Record<string, unknown> = Record<string, unknown>> = Omit<BranchStep<TName, TConfig, TStepCtx>, "type">;
92
+ /**
93
+ * Define a leaf step (executable, no children).
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * const createProviders = defineLeafStep({
98
+ * name: "providers",
99
+ * meta: { label: "Create Providers", description: "Creates I/O Events providers" },
100
+ * run: async ({ config, stepContext }) => {
101
+ * const { eventsClient } = stepContext;
102
+ * return eventsClient.createProvider(config.eventing);
103
+ * },
104
+ * });
105
+ * ```
106
+ */
107
+ declare function defineLeafStep<TName extends string, TConfig extends CommerceAppConfigOutputModel, TStepCtx extends Record<string, unknown> = Record<string, unknown>, TOutput$1 = unknown>(options: LeafStepOptions<TName, TConfig, TStepCtx, TOutput$1>): LeafStep<TName, TConfig, TStepCtx, TOutput$1>;
108
+ /**
109
+ * Define a branch step (container with children, no runner).
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * const eventing = defineBranchStep({
114
+ * name: "eventing",
115
+ * meta: { label: "Eventing", description: "Sets up I/O Events" },
116
+ * when: hasEventing,
117
+ * context: async (ctx) => ({ eventsClient: await createEventsClient(ctx) }),
118
+ * children: [commerceEventsStep, externalEventsStep],
119
+ * });
120
+ * ```
121
+ */
122
+ declare function defineBranchStep<TName extends string, TConfig extends CommerceAppConfigOutputModel, TStepCtx extends Record<string, unknown> = Record<string, unknown>>(options: BranchStepOptions<TName, TConfig, TStepCtx>): BranchStep<TName, TConfig, TStepCtx>;
123
+ /** Infer the output type from a leaf step. */
124
+ type InferStepOutput<TStep> = TStep extends LeafStep<infer _TName, infer _TConfig, infer _TStepCtx, infer TOutput> ? Awaited<TOutput> : never;
125
+ //#endregion
126
+ //#region source/management/installation/workflow/types.d.ts
127
+ /** Status of a step execution. */
128
+ type ExecutionStatus = "pending" | "in-progress" | "succeeded" | "failed";
129
+ /** Overall installation status. */
130
+ type InstallationStatus = "pending" | "in-progress" | "succeeded" | "failed";
131
+ /** A structured error with path to the failing step. */
132
+ type InstallationError<TPayload = unknown> = {
133
+ /** Path to the step that failed (e.g., ["eventing", "commerce", "providers"]). */
134
+ path: string[];
135
+ /** Error key for easy identification. */
136
+ key: string;
137
+ /** Human-readable error message. */
138
+ message?: string;
139
+ /** Additional error payload. */
140
+ payload?: TPayload;
141
+ };
142
+ /** Status of a step in the installation tree. */
143
+ type StepStatus = {
144
+ /** Step name (unique among siblings). */
145
+ name: string;
146
+ /** Unique step identifier (e.g., UUID). */
147
+ id: string;
148
+ /** Full path from root to this step. */
149
+ path: string[];
150
+ /** Step metadata (for display purposes). */
151
+ meta: StepMeta;
152
+ /** Current execution status. */
153
+ status: ExecutionStatus;
154
+ /** Child step statuses (empty for leaf steps). */
155
+ children: StepStatus[];
156
+ };
157
+ /** Data collected during installation as a nested structure following step paths. */
158
+ type InstallationData = {
159
+ [key: string]: unknown | InstallationData;
160
+ };
161
+ /** Base properties shared by all installation states. */
162
+ type InstallationStateBase = {
163
+ /** Unique installation identifier. */
164
+ id: string;
165
+ /** Root step status. */
166
+ step: StepStatus;
167
+ /** Results from executed leaf steps, keyed by path. */
168
+ data: InstallationData | null;
169
+ };
170
+ /** Installation state when in progress. */
171
+ type InProgressInstallationState = InstallationStateBase & {
172
+ status: "in-progress";
173
+ /** ISO timestamp when installation started. */
174
+ startedAt: string;
175
+ };
176
+ /** Installation state when completed successfully. */
177
+ type SucceededInstallationState = InstallationStateBase & {
178
+ status: "succeeded";
179
+ /** ISO timestamp when installation started. */
180
+ startedAt: string;
181
+ /** ISO timestamp when installation completed. */
182
+ completedAt: string;
183
+ };
184
+ /** Installation state when failed. */
185
+ type FailedInstallationState = InstallationStateBase & {
186
+ status: "failed";
187
+ /** ISO timestamp when installation started. */
188
+ startedAt: string;
189
+ /** ISO timestamp when installation failed. */
190
+ completedAt: string;
191
+ /** Error information about the failure. */
192
+ error: InstallationError;
193
+ };
194
+ /**
195
+ * The full installation state (persisted and returned by status endpoints).
196
+ * Discriminated union by `status` field.
197
+ */
198
+ type InstallationState = InProgressInstallationState | SucceededInstallationState | FailedInstallationState;
199
+ /** Type guard for pending installation state. */
200
+ /** Type guard for in-progress installation state. */
201
+ declare function isInProgressState(state: InstallationState): state is InProgressInstallationState;
202
+ /** Type guard for succeeded installation state. */
203
+ declare function isSucceededState(state: InstallationState): state is SucceededInstallationState;
204
+ /** Type guard for failed installation state. */
205
+ declare function isFailedState(state: InstallationState): state is FailedInstallationState;
206
+ /** Type guard for completed installation state (succeeded or failed). */
207
+ declare function isCompletedState(state: InstallationState): state is SucceededInstallationState | FailedInstallationState;
208
+ //#endregion
209
+ //#region source/management/installation/workflow/hooks.d.ts
210
+ /** Hook function that receives an event and the current state. */
211
+ type HookFunction<TEvent> = (event: TEvent, state: InstallationState) => void | Promise<void>;
212
+ /** Hook function that only receives the current state. */
213
+ type InstallationHook = (state: InstallationState) => void | Promise<void>;
214
+ /** Base event payload for step events. */
215
+ type StepEvent = {
216
+ /** Full path to the step (e.g., ["eventing", "commerce", "providers"]). */
217
+ path: string[];
218
+ /** Step name (last element of path, for convenience). */
219
+ stepName: string;
220
+ /** Whether this is a leaf step (executable) or branch step (container). */
221
+ isLeaf: boolean;
222
+ };
223
+ /** Event payload when a step starts execution. */
224
+ type StepStartedEvent = StepEvent;
225
+ /** Event payload when a step succeeds. */
226
+ type StepSucceededEvent = StepEvent & {
227
+ /** Result returned by the step (only for leaf steps). */
228
+ result: unknown;
229
+ };
230
+ /** Event payload when a step fails. */
231
+ type StepFailedEvent = StepEvent & {
232
+ /** Error information. */
233
+ error: InstallationError;
234
+ };
235
+ /** Lifecycle hooks for installation execution. */
236
+ type InstallationHooks = {
237
+ onInstallationStart?: InstallationHook;
238
+ onInstallationSuccess?: InstallationHook;
239
+ onInstallationFailure?: InstallationHook;
240
+ onStepStart?: HookFunction<StepStartedEvent>;
241
+ onStepSuccess?: HookFunction<StepSucceededEvent>;
242
+ onStepFailure?: HookFunction<StepFailedEvent>;
243
+ };
244
+ //#endregion
245
+ //#region source/management/installation/workflow/runner.d.ts
246
+ /** Options for creating an initial installation state. */
247
+ type CreateInitialStateOptions = {
248
+ /** The root branch step to build the state from. */
249
+ rootStep: BranchStep;
250
+ /** The app configuration used to determine applicable steps. */
251
+ config: CommerceAppConfigOutputModel;
252
+ };
253
+ /** Options for executing a workflow. */
254
+ type ExecuteWorkflowOptions = {
255
+ /** The root branch step to execute. */
256
+ rootStep: BranchStep;
257
+ /** Shared installation context (params, logger, etc.). */
258
+ installationContext: InstallationContext;
259
+ /** The app configuration. */
260
+ config: CommerceAppConfigOutputModel;
261
+ /** The initial installation state (with all steps pending). */
262
+ initialState: InProgressInstallationState;
263
+ /** Lifecycle hooks for status change notifications. */
264
+ hooks?: InstallationHooks;
265
+ };
266
+ /**
267
+ * Creates an initial installation state from a root step and config.
268
+ *
269
+ * Filters steps based on their `when` conditions and builds a
270
+ * tree structure with all steps set to "pending".
271
+ */
272
+ declare function createInitialState(options: CreateInitialStateOptions): InProgressInstallationState;
273
+ /**
274
+ * Executes a workflow from an initial state. Returns the final state (never throws).
275
+ */
276
+ declare function executeWorkflow(options: ExecuteWorkflowOptions): Promise<SucceededInstallationState | FailedInstallationState>;
277
+ //#endregion
278
+ //#region source/management/installation/custom-installation/define.d.ts
279
+ /**
280
+ * Handler function type for custom installation steps.
281
+ *
282
+ * @param config - The validated commerce app configuration
283
+ * @param context - Execution context with logger, params, and other utilities
284
+ * @returns The result of the installation step (can be any value or Promise)
285
+ */
286
+ type CustomInstallationStepHandler<TResult = unknown> = (config: CommerceAppConfigOutputModel, context: ExecutionContext) => TResult | Promise<TResult>;
287
+ /**
288
+ * Define a custom installation step with type-safe parameters.
289
+ *
290
+ * This helper provides type safety and IDE autocompletion for custom installation scripts.
291
+ * The handler function receives properly typed `config` and `context` parameters.
292
+ *
293
+ * @param handler - The installation step handler function
294
+ * @returns The same handler function (for use as default export)
295
+ *
296
+ * @example
297
+ * ```typescript
298
+ * import { defineCustomInstallationStep } from "@adobe/aio-commerce-lib-app/management";
299
+ *
300
+ * export default defineCustomInstallationStep(async (config, context) => {
301
+ * const { logger, params } = context;
302
+ *
303
+ * logger.info(`Setting up ${config.metadata.displayName}...`);
304
+ *
305
+ * // Your installation logic here
306
+ * // TypeScript will provide autocompletion for config and context
307
+ *
308
+ * return {
309
+ * status: "success",
310
+ * message: "Setup completed",
311
+ * };
312
+ * });
313
+ * ```
314
+ */
315
+ declare function defineCustomInstallationStep<TResult = unknown>(handler: CustomInstallationStepHandler<TResult>): CustomInstallationStepHandler<TResult>;
316
+ //#endregion
317
+ //#region source/management/installation/runner.d.ts
318
+ /** Options for creating an initial installation state. */
319
+ type CreateInitialInstallationStateOptions = {
320
+ /** The app configuration used to determine applicable steps. */
321
+ config: CommerceAppConfigOutputModel;
322
+ };
323
+ /** Options for running an installation. */
324
+ type RunInstallationOptions = {
325
+ /** Shared installation context (params, logger, etc.). */
326
+ installationContext: InstallationContext;
327
+ /** The app configuration. */
328
+ config: CommerceAppConfigOutputModel;
329
+ /** The initial installation state (with all steps pending). */
330
+ initialState: InProgressInstallationState;
331
+ /** Lifecycle hooks for status change notifications. */
332
+ hooks?: InstallationHooks;
333
+ };
334
+ /**
335
+ * Creates an initial installation state from the config and step definitions.
336
+ * Filters steps based on their `when` conditions and builds a tree structure
337
+ * with all steps set to "pending".
338
+ */
339
+ declare function createInitialInstallationState(options: CreateInitialInstallationStateOptions): InProgressInstallationState;
340
+ /**
341
+ * Runs the full installation workflow. Returns the final state (never throws).
342
+ */
343
+ declare function runInstallation(options: RunInstallationOptions): Promise<SucceededInstallationState | FailedInstallationState>;
344
+ //#endregion
345
+ export { BranchStep as A, defineBranchStep as B, StepStatus as C, isInProgressState as D, isFailedState as E, LeafStep as F, isBranchStep as H, LeafStepOptions as I, Step as L, ExecutionContext as M, InferStepOutput as N, isSucceededState as O, InstallationContext as P, StepContextFactory as R, InstallationStatus as S, isCompletedState as T, isLeafStep as U, defineLeafStep as V, FailedInstallationState as _, CustomInstallationStepHandler as a, InstallationError as b, ExecuteWorkflowOptions as c, InstallationHooks as d, StepEvent as f, ExecutionStatus as g, StepSucceededEvent as h, runInstallation as i, BranchStepOptions as j, AnyStep as k, createInitialState as l, StepStartedEvent as m, RunInstallationOptions as n, defineCustomInstallationStep as o, StepFailedEvent as p, createInitialInstallationState as r, CreateInitialStateOptions as s, CreateInitialInstallationStateOptions as t, executeWorkflow as u, InProgressInstallationState as v, SucceededInstallationState as w, InstallationState as x, InstallationData as y, StepMeta as z };
@@ -0,0 +1,3 @@
1
+ import "../app-CMpx3D7Y.mjs";
2
+ import { A as BranchStep, B as defineBranchStep, C as StepStatus, D as isInProgressState, E as isFailedState, F as LeafStep, H as isBranchStep, I as LeafStepOptions, L as Step, M as ExecutionContext, N as InferStepOutput, O as isSucceededState, P as InstallationContext, R as StepContextFactory, S as InstallationStatus, T as isCompletedState, U as isLeafStep, V as defineLeafStep, _ as FailedInstallationState, a as CustomInstallationStepHandler, b as InstallationError, c as ExecuteWorkflowOptions, d as InstallationHooks, f as StepEvent, g as ExecutionStatus, h as StepSucceededEvent, i as runInstallation, j as BranchStepOptions, k as AnyStep, l as createInitialState, m as StepStartedEvent, n as RunInstallationOptions, o as defineCustomInstallationStep, p as StepFailedEvent, r as createInitialInstallationState, s as CreateInitialStateOptions, t as CreateInitialInstallationStateOptions, u as executeWorkflow, v as InProgressInstallationState, w as SucceededInstallationState, x as InstallationState, y as InstallationData, z as StepMeta } from "../index-DQepSWYP.mjs";
3
+ export { AnyStep, BranchStep, BranchStepOptions, CreateInitialInstallationStateOptions, CreateInitialStateOptions, CustomInstallationStepHandler, ExecuteWorkflowOptions, ExecutionContext, ExecutionStatus, FailedInstallationState, InProgressInstallationState, InferStepOutput, InstallationContext, InstallationData, InstallationError, InstallationHooks, InstallationState, InstallationStatus, LeafStep, LeafStepOptions, RunInstallationOptions, Step, StepContextFactory, StepEvent, StepFailedEvent, StepMeta, StepStartedEvent, StepStatus, StepSucceededEvent, SucceededInstallationState, createInitialInstallationState, createInitialState, defineBranchStep, defineCustomInstallationStep, defineLeafStep, executeWorkflow, isBranchStep, isCompletedState, isFailedState, isInProgressState, isLeafStep, isSucceededState, runInstallation };
@@ -0,0 +1 @@
1
+ import{a as isFailedState,i as isCompletedState,n as runInstallation,o as isInProgressState,r as defineCustomInstallationStep,s as isSucceededState,t as createInitialInstallationState}from"../management-BM2WcbV6.mjs";export{createInitialInstallationState,defineCustomInstallationStep,isCompletedState,isFailedState,isInProgressState,isSucceededState,runInstallation};
@@ -0,0 +1,2 @@
1
+ import{t as stringifyError}from"./error-hBHRgZ9R.mjs";import util from"util";import{resolveAuthParams}from"@adobe/aio-commerce-lib-auth";import{resolveCommerceHttpClientParams,resolveIoEventsHttpClientParams}from"@adobe/aio-commerce-lib-api";import{createCustomCommerceEventsApiClient,createEventProvider,createEventSubscription,getAllEventProviders,getAllEventSubscriptions,updateEventingConfiguration}from"@adobe/aio-commerce-lib-events/commerce";import{createCustomAdobeIoEventsApiClient,createEventMetadataForProvider,createEventProvider as createEventProvider$1,createRegistration,getAllEventProviders as getAllEventProviders$1,getAllRegistrations}from"@adobe/aio-commerce-lib-events/io-events";async function callHook(hooks,hookName,...args){let hook=hooks?.[hookName];hook&&await hook(...args)}function isLeafStep(step){return step.type===`leaf`}function isBranchStep(step){return step.type===`branch`}function defineLeafStep(options){return{type:`leaf`,name:options.name,meta:options.meta,when:options.when,run:options.run}}function defineBranchStep(options){return{type:`branch`,name:options.name,meta:options.meta,when:options.when,context:options.context,children:options.children}}function nowIsoString(){return new Date().toISOString()}function setAtPath(data,path,value){let lastKey=path.at(-1);if(!lastKey)return;let current=data;for(let i=0;i<path.length-1;i++){let key=path[i];current[key]??={},current=current[key]}current[lastKey]=value}function getAtPath(data,path){let current=data;for(let key of path){if(typeof current!=`object`||!current)return;current=current[key]}return current}function createInstallationError(err,path,key=`STEP_EXECUTION_FAILED`){return{path,key,message:err instanceof Error?err.message:String(err)}}function createSucceededState(base){return{...base,status:`succeeded`,completedAt:nowIsoString()}}function createFailedState(base,error){return{...base,status:`failed`,completedAt:nowIsoString(),error}}function createInitialState(options){let{rootStep,config}=options;return{id:crypto.randomUUID(),startedAt:nowIsoString(),status:`in-progress`,step:buildInitialStepStatus(rootStep,config,[]),data:null}}async function executeWorkflow(options){let{rootStep,installationContext,config,initialState,hooks}=options,step=structuredClone(initialState.step),context={installationContext,config,id:initialState.id,startedAt:initialState.startedAt,step,data:null,error:null,hooks};await callHook(hooks,`onInstallationStart`,snapshot(context));try{await executeStep(rootStep,context.step,{},context);let succeeded=createSucceededState({id:context.id,startedAt:context.startedAt,step:context.step,data:context.data});return await callHook(hooks,`onInstallationSuccess`,succeeded),succeeded}catch(err){let error=context.error??createInstallationError(err,[],`INSTALLATION_FAILED`),failed=createFailedState({step:context.step,id:context.id,startedAt:context.startedAt,data:context.data},error);return await callHook(hooks,`onInstallationFailure`,failed),failed}}function buildInitialStepStatus(step,config,parentPath){let path=[...parentPath,step.name],children=[];if(isBranchStep(step)&&step.children.length>0)for(let child of step.children)child.when&&!child.when(config)||children.push(buildInitialStepStatus(child,config,path));return{id:crypto.randomUUID(),name:step.name,path,meta:step.meta,status:`pending`,children}}function snapshot(context){return{id:context.id,startedAt:context.startedAt,status:`in-progress`,step:context.step,data:context.data}}async function executeStep(step,stepStatus,inherited,context){let{path}=stepStatus,isLeaf=isLeafStep(step);stepStatus.status=`in-progress`,await callHook(context.hooks,`onStepStart`,{path,stepName:step.name,isLeaf},snapshot(context));try{isBranchStep(step)?await executeBranchStep(step,stepStatus,inherited,context):isLeafStep(step)&&await executeLeafStep(step,stepStatus,inherited,context),stepStatus.status=`succeeded`,context.data??={},await callHook(context.hooks,`onStepSuccess`,{path,stepName:step.name,isLeaf,result:getAtPath(context.data,path)},snapshot(context))}catch(err){throw stepStatus.status=`failed`,context.error??=createInstallationError(err,path),await callHook(context.hooks,`onStepFailure`,{path,stepName:step.name,isLeaf,error:context.error},snapshot(context)),err}}async function executeBranchStep(step,stepStatus,inherited,context){let childContext=inherited;if(step.context){let stepContext=await step.context(context.installationContext);childContext={...inherited,...stepContext}}for(let child of stepStatus.children){let childStep=step.children.find(c=>c.name===child.name);if(!childStep)throw Error(`Step "${child.name}" not found`);await executeStep(childStep,child,childContext,context)}}async function executeLeafStep(step,stepStatus,inherited,context){let executionContext={...context.installationContext,...inherited},result=await step.run(context.config,executionContext);context.data??={},setAtPath(context.data,stepStatus.path,result)}function isInProgressState(state){return state.status===`in-progress`}function isSucceededState(state){return state.status===`succeeded`}function isFailedState(state){return state.status===`failed`}function isCompletedState(state){return state.status===`succeeded`||state.status===`failed`}function hasCustomInstallationSteps(config){return Array.isArray(config?.installation?.customInstallationSteps)&&config.installation.customInstallationSteps.length>0}function createCustomScriptStep(scriptConfig){let{script,name,description}=scriptConfig;return defineLeafStep({name,meta:{label:name,description},run:async(config,context)=>{let{logger}=context,customScripts=context.customScripts||{};logger.info(`Executing custom installation script: ${name}`),logger.debug(`Script path: ${script}`);let scriptModule=customScripts[script];if(!scriptModule)throw 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.`);if(typeof scriptModule!=`object`||!(`default`in scriptModule))throw Error(`Script ${script} must export a default function. Use defineCustomInstallationStep helper.`);let runFunction=scriptModule.default;if(typeof runFunction!=`function`)throw Error(`Script ${script} default export must be a function, got ${typeof runFunction}`);let scriptResult=await runFunction(config,context);return logger.info(`Successfully executed script: ${name}`),{script,data:scriptResult}}})}function createCustomScriptSteps(config){return hasCustomInstallationSteps(config)?config.installation.customInstallationSteps.map(scriptConfig=>createCustomScriptStep(scriptConfig)):[]}function hasCustomInstallation(config){return hasCustomInstallationSteps(config)}const customInstallationStepBase=defineBranchStep({name:`Custom Installation Steps`,meta:{label:`Custom Installation Steps`,description:`Executes custom installation scripts defined in the application configuration`},when:hasCustomInstallation,children:[]});function createCustomInstallationStep(config){return{...customInstallationStepBase,children:createCustomScriptSteps(config)}}function defineCustomInstallationStep(handler){return handler}function inspect(obj,params={}){return util.inspect(obj,{depth:null,...params})}const COMMERCE_PROVIDER_TYPE=`dx_commerce_events`,EXTERNAL_PROVIDER_TYPE=`3rd_party_custom_events`,PROVIDER_TYPE_TO_LABEL={[COMMERCE_PROVIDER_TYPE]:`Commerce`,[EXTERNAL_PROVIDER_TYPE]:`External`};function generateInstanceId(metadata,provider){let slugLabel=provider.label.toLowerCase().replace(/\s+/g,`-`);return`${metadata.id}-${provider.key??slugLabel}`}function findExistingProvider(allProviders,instanceId){return allProviders.find(provider=>provider.instance_id===instanceId)??null}function findExistingProviderMetadata(allMetadata,eventName){return allMetadata.find(meta=>meta.event_code===eventName)??null}function findExistingRegistrations(allRegistrations,clientId,name){return allRegistrations.find(reg=>reg.client_id===clientId&&reg.name===name)}function getNamespacedEvent(metadata,name){return`${metadata.id}.${name}`}function getIoEventCode(name,providerType){return providerType===COMMERCE_PROVIDER_TYPE?`com.adobe.commerce.${name}`:name}function getRegistrationName(provider,runtimeAction){let providerLabel=PROVIDER_TYPE_TO_LABEL[provider.provider_metadata]??`Unknown`,[packageName,actionName]=runtimeAction.split(`/`).map(kebabToTitleCase);return`${providerLabel} Event Registration: ${actionName} (${packageName})`}function getRegistrationDescription(provider,events,runtimeAction){return[`This registration was automatically created by @adobe/aio-commerce-lib-app. `,`It belongs to the provider "${provider.label}" (instance ID: ${provider.instance_id}). `,`It routes ${events.length} event(s) to the runtime action "${runtimeAction}".`].join(`
2
+ `)}function groupEventsByRuntimeActions(events){let actionEventsMap=new Map;for(let event of events)for(let runtimeAction of event.runtimeActions){let existingEvents=actionEventsMap.get(runtimeAction)??[];actionEventsMap.set(runtimeAction,[...existingEvents,event])}return actionEventsMap}function kebabToTitleCase(str){return str.split(`-`).map(word=>word.charAt(0).toUpperCase()+word.slice(1)).join(` `)}function findExistingSubscription(allSubscriptions,eventName){return allSubscriptions.get(eventName)??null}function makeWorkspaceConfig(context){let{appData,params}=context,{consumerOrgId,orgName,projectId,projectName,projectTitle,workspaceId,workspaceName,workspaceTitle}=appData,authParams=resolveAuthParams(params);if(authParams.strategy!==`ims`)throw Error(`Failed to resolve IMS authentication parameters from the runtime action inputs.`);let{clientId,clientSecrets,technicalAccountEmail,technicalAccountId,imsOrgId,scopes}=authParams;return{project:{id:projectId,name:projectName,title:projectTitle,org:{id:consumerOrgId,name:orgName,ims_org_id:imsOrgId},workspace:{id:workspaceId,name:workspaceName,title:workspaceTitle,action_url:`https://${process.env.__OW_NAMESPACE}.adobeioruntime.net`,app_url:`https://${process.env.__OW_NAMESPACE}.adobeio-static.net`,details:{credentials:[{id:`000000`,name:`aio-${workspaceId}`,integration_type:`oauth_server_to_server`,oauth_server_to_server:{client_id:clientId,client_secrets:clientSecrets,technical_account_email:technicalAccountEmail,technical_account_id:technicalAccountId,scopes:scopes.map(scope=>scope.trim())}}]}}}}}async function getIoEventsExistingData(context){let{ioEventsClient,appData}=context,appCredentials={consumerOrgId:appData.consumerOrgId,projectId:appData.projectId,workspaceId:appData.workspaceId},{_embedded:{providers:existingProviders}}=await ioEventsClient.getAllEventProviders({consumerOrgId:appData.consumerOrgId,withEventMetadata:!0}),providersWithMetadata=existingProviders.map(providerHal=>{let{_embedded,_links,...providerData}=providerHal,actualMetadata=(_embedded?.eventmetadata??[]).map(({_embedded:_embedded$1,_links:_links$1,...meta})=>({...meta,sample:_embedded$1?.sample_event??null}));return{...providerData,metadata:actualMetadata}}),{_embedded:{registrations:registrationsHal}}=await ioEventsClient.getAllRegistrations(appCredentials);return{providersWithMetadata,registrations:registrationsHal.map(({_links,...reg})=>reg)}}async function getCommerceEventingExistingData(context){let{commerceEventsClient}=context,existingProviders=await commerceEventsClient.getAllEventProviders(),existingSubscriptions=await commerceEventsClient.getAllEventSubscriptions();return{isDefaultWorkspaceConfigurationEmpty:existingProviders.some(provider=>!(`id`in provider)&&!provider.workspace_configuration?.trim()),providers:existingProviders,subscriptions:new Map(existingSubscriptions.map(subscription=>[subscription.name,subscription]))}}async function createIoEventProvider(params){let{context,provider}=params,{appData,ioEventsClient,logger}=context,appCredentials={consumerOrgId:appData.consumerOrgId,projectId:appData.projectId,workspaceId:appData.workspaceId};return logger.info(`Creating provider "${provider.label}" with instance ID "${provider.instanceId}"`),ioEventsClient.createEventProvider({...appCredentials,label:provider.label,providerType:provider.type,instanceId:provider.instanceId,description:provider.description}).then(res=>(logger.info(`Provider "${provider.label}" created with ID '${res.id}'`),res)).catch(error=>{throw logger.error(`Failed to create provider "${provider.label}": ${stringifyError(error)}`),error})}async function createOrGetIoEventProvider(params,existingData){let{context,provider}=params,{logger}=context,{instanceId,...providerData}=provider,existing=findExistingProvider(existingData,instanceId);return existing?(logger.info(`Provider "${provider.label}" already exists with ID "${existing.id}", skipping creation.`),existing):createIoEventProvider({context,provider:{...providerData,instanceId}})}async function createIoEventProviderEventMetadata(params){let{context,event,provider,type,metadata}=params,{appData,ioEventsClient,logger}=context,appCredentials={consumerOrgId:appData.consumerOrgId,projectId:appData.projectId,workspaceId:appData.workspaceId},eventCode=getIoEventCode(getNamespacedEvent(metadata,event.name),type);return logger.info(`Creating event metadata (${eventCode}) for provider "${provider.label}" (instance ID: ${provider.instance_id}))`),ioEventsClient.createEventMetadataForProvider({...appCredentials,providerId:provider.id,label:event.label,description:event.description,eventCode}).then(res=>(logger.info(`Event metadata "${event.label}" created for provider "${provider.label}"`),res)).catch(error=>{throw logger.error(`Failed to create event metadata "${event.label}" for provider "${provider.label}": ${stringifyError(error)}`),error})}async function createOrGetIoProviderEventMetadata(params,existingData){let{context,event,provider,type,metadata}=params,{logger}=context,existing=findExistingProviderMetadata(existingData,getIoEventCode(getNamespacedEvent(metadata,event.name),type));return existing?(logger.info(`Event metadata "${event.label}" already exists for provider "${provider.label}" (instance ID: ${provider.instance_id}), skipping creation.`),existing):createIoEventProviderEventMetadata(params)}async function createIoEventRegistration(params){let{context,events,provider,runtimeAction,metadata}=params,{appData,ioEventsClient,logger,params:runtimeParams}=context,appCredentials={consumerOrgId:appData.consumerOrgId,projectId:appData.projectId,workspaceId:appData.workspaceId};logger.info(`Creating registration(s) to runtime action "${runtimeAction}" for ${events.length} event(s) with provider "${provider.label}" (instance ID: ${provider.instance_id}))`);let name=getRegistrationName(provider,runtimeAction),description=getRegistrationDescription(provider,events,runtimeAction);return ioEventsClient.createRegistration({...appCredentials,name,clientId:runtimeParams.AIO_COMMERCE_AUTH_IMS_CLIENT_ID,description,deliveryType:`webhook`,runtimeAction,enabled:!0,eventsOfInterest:events.map(event=>({providerId:provider.id,eventCode:getIoEventCode(getNamespacedEvent(metadata,event.name),provider.provider_metadata)}))}).then(res=>(logger.info(`Registration "${name}" created for provider "${provider.label}" with ID '${res.id}'`),res)).catch(error=>{throw logger.error(`Failed to create registration "${name}" for provider "${provider.label}": ${stringifyError(error)}`),error})}async function createOrGetIoEventRegistration(params,registrations){let{context,provider,runtimeAction}=params,{logger,params:runtimeParams}=context,name=getRegistrationName(provider,runtimeAction),existing=findExistingRegistrations(registrations,runtimeParams.AIO_COMMERCE_AUTH_IMS_CLIENT_ID,name);return existing?(logger.info(`Registration "${name}" already exists for provider "${provider.label}" with ID '${existing.id}', skipping creation.`),existing):createIoEventRegistration(params)}async function configureCommerceEventing(params,existingData){let{context,config}=params,{commerceEventsClient,logger}=context;logger.info(`Starting configuration of the Commerce Eventing Module`);let updateParams={...config,enabled:!0};if(existingData.isDefaultWorkspaceConfigurationEmpty){if(!config.workspaceConfiguration){let message=`Workspace configuration is required to enable Commerce Eventing when there is not an existing one.`;throw logger.error(message),Error(message)}logger.info(`Default provider workspace configuration already present, it will not be overriden`);let{workspaceConfiguration,...rest}=updateParams;updateParams=rest}return logger.info(`Updating Commerce Eventing configuration with provided workspace configuration.`),commerceEventsClient.updateEventingConfiguration(updateParams).then(success=>{if(success){logger.info(`Commerce Eventing Module configured successfully.`);return}throw Error(`Something went wrong while configuring Commerce Eventing Module. Response was not successful but no error was thrown.`)}).catch(err=>{throw logger.error(`Failed to configure Commerce Eventing Module: ${stringifyError(err)}`),err})}async function createCommerceProvider(params){let{context,provider}=params,{commerceEventsClient,logger}=context;return logger.info(`Creating Commerce provider "${provider.label}" with instance ID "${provider.instance_id}"`),commerceEventsClient.createEventProvider({providerId:provider.id,instanceId:provider.instance_id,label:provider.label,description:provider.description,associatedWorkspaceConfiguration:provider.workspaceConfiguration}).then(res=>(logger.info(`Commerce provider "${provider.label}" created with ID '${res.provider_id}'`),res)).catch(err=>{throw logger.error(`Failed to create Commerce provider "${provider.label}": ${stringifyError(err)}`),err})}async function createOrGetCommerceProvider(params,existingProviders){let{context,provider}=params,{logger}=context,existing=findExistingProvider(existingProviders,provider.instance_id);return existing?(logger.info(`Provider "${provider.label}" already exists with ID "${existing.id}", skipping creation.`),existing):createCommerceProvider(params)}async function createCommerceEventSubscription(params){let{context,metadata,provider,event}=params,{logger,commerceEventsClient}=context,eventName=getNamespacedEvent(metadata,event.config.name);logger.info(`Creating event subscription for event "${event.config.name}" to provider "${provider.label}" (instance ID: ${provider.instance_id})`);let eventSpec={name:eventName,parent:event.config.name,fields:event.config.fields,providerId:provider.id,destination:event.config.destination,hipaaAuditRequired:event.config.hipaaAuditRequired,prioritary:event.config.prioritary,force:event.config.force};return logger.debug(`Event subscription specification for event "${event.config.name}": ${inspect(eventSpec)}`),commerceEventsClient.createEventSubscription(eventSpec).then(_res=>(logger.info(`Created event subscription for event "${event.config.name}" to provider "${provider.label} (instance ID: ${provider.instance_id})"`),eventSpec)).catch(err=>{throw logger.error(`Failed to create event subscription for event "${event.config.name}" to provider "${provider.label}": ${stringifyError(err)}`),err})}async function createOrGetCommerceEventSubscription(params,existingData){let{context,metadata,event}=params,{logger}=context,existing=findExistingSubscription(existingData,getNamespacedEvent(metadata,event.config.name));return existing?(logger.info(`Subscription for event "${event.config.name}" already exists, skipping creation.`),{name:existing.name,parent:existing.parent,fields:existing.fields,providerId:existing.provider_id}):createCommerceEventSubscription(params)}async function onboardIoEvents(params,existingData){let{providersWithMetadata,registrations}=existingData,{context,metadata,provider,providerType,events}=params,instanceId=generateInstanceId(metadata,provider),providerData=await createOrGetIoEventProvider({context,provider:{...provider,instanceId,type:providerType}},providersWithMetadata),metadataPromises=events.map(event=>createOrGetIoProviderEventMetadata({metadata,context,type:providerType,provider:providerData,event},providersWithMetadata.find(p=>p.id===providerData.id)?.metadata??[])),actionEventsMap=groupEventsByRuntimeActions(events),registrationPromises=Array.from(actionEventsMap.entries()).map(([runtimeAction,groupedEvents])=>createOrGetIoEventRegistration({metadata,context,events:groupedEvents,provider:providerData,runtimeAction},registrations)),[metadataData,registrationsData]=await Promise.all([Promise.all(metadataPromises),Promise.all(registrationPromises)]);return{providerData,eventsData:events.map((event,index)=>{let eventRegistrations=event.runtimeActions.map(runtimeAction=>registrationsData[Array.from(actionEventsMap.keys()).indexOf(runtimeAction)]);return{config:{...event,providerType},data:{metadata:metadataData[index],registrations:eventRegistrations}}})}}async function onboardCommerceEventing(params,existingData){let{context,metadata,ioData}=params,{events,provider,workspaceConfiguration}=ioData,instanceId=provider.instance_id,subscriptions=[];await configureCommerceEventing({context,config:{workspaceConfiguration}},existingData);let{workspace_configuration:_,...commerceProviderData}=await createOrGetCommerceProvider({context,provider:{id:provider.id,instance_id:instanceId,label:provider.label,description:provider.description,workspaceConfiguration}},existingData.providers);for(let event of events)subscriptions.push(await createOrGetCommerceEventSubscription({context,metadata,provider,event},existingData.subscriptions));return{commerceProvider:commerceProviderData,subscriptions}}function hasCommerceEvents(config){return Array.isArray(config?.eventing?.commerce)&&config.eventing.commerce.length>0}const commerceEventsStep=defineLeafStep({name:`commerce`,meta:{label:`Configure Commerce Events`,description:`Sets up I/O Events for Adobe Commerce event sources`},when:hasCommerceEvents,run:async(config,context)=>{let{logger}=context;logger.debug(`Starting installation of Commerce Events with config:`,config);let stepData=[],workspaceConfiguration=JSON.stringify(makeWorkspaceConfig(context)),existingIoEventsData=await getIoEventsExistingData(context),commerceEventingExistingData=await getCommerceEventingExistingData(context);for(let{provider,events}of config.eventing.commerce){let{providerData,eventsData}=await onboardIoEvents({context,metadata:config.metadata,provider,events,providerType:COMMERCE_PROVIDER_TYPE},existingIoEventsData),{commerceProvider,subscriptions}=await onboardCommerceEventing({context,metadata:config.metadata,provider,ioData:{provider:providerData,events:eventsData,workspaceConfiguration}},commerceEventingExistingData);stepData.push({provider:{config:provider,data:{ioEvents:providerData,commerce:commerceProvider,events:eventsData.map(({config:config$1,data},index)=>({config:config$1,data:{...data,subscription:subscriptions[index]}}))}}})}return logger.debug(`Completed Commerce Events installation step.`),stepData}});function createCommerceEventsApiClient(params){let commerceClientParams=resolveCommerceHttpClientParams(params,{tryForwardAuthProvider:!0});return commerceClientParams.fetchOptions??={},commerceClientParams.fetchOptions.timeout=1e3*60*2,createCustomCommerceEventsApiClient(commerceClientParams,{createEventProvider,getAllEventProviders,createEventSubscription,getAllEventSubscriptions,updateEventingConfiguration})}function createIoEventsApiClient(params){let ioEventsClientParams=resolveIoEventsHttpClientParams(params);return ioEventsClientParams.fetchOptions??={},ioEventsClientParams.fetchOptions.timeout=1e3*60*2,createCustomAdobeIoEventsApiClient(ioEventsClientParams,{createEventProvider:createEventProvider$1,createEventMetadataForProvider,createRegistration,getAllEventProviders:getAllEventProviders$1,getAllRegistrations})}function createEventsStepContext(installation){let{params}=installation,commerceEventsClient=null,ioEventsClient=null;return{get commerceEventsClient(){return commerceEventsClient===null&&(commerceEventsClient=createCommerceEventsApiClient(params)),commerceEventsClient},get ioEventsClient(){return ioEventsClient===null&&(ioEventsClient=createIoEventsApiClient(params)),ioEventsClient}}}function hasExternalEvents(config){return Array.isArray(config?.eventing?.external)&&config.eventing.external.length>0}const externalEventsStep=defineLeafStep({name:`external`,meta:{label:`Configure External Events`,description:`Sets up I/O Events for external event sources`},when:hasExternalEvents,run:async(config,context)=>{let{logger}=context;logger.debug(`Starting installation of External Events with config:`,config);let stepData=[],existingIoEventsData=await getIoEventsExistingData(context);for(let{provider,events}of config.eventing.external){let{providerData,eventsData}=await onboardIoEvents({context,metadata:config.metadata,provider,events,providerType:EXTERNAL_PROVIDER_TYPE},existingIoEventsData);stepData.push({provider:{config:provider,data:{ioEvents:providerData,events:{config:events,data:eventsData}}}})}return logger.debug(`Completed External Events installation step.`),stepData}});function hasEventing(config){return config.eventing!==void 0&&(hasCommerceEvents(config)||hasExternalEvents(config))}const eventingStep=defineBranchStep({name:`eventing`,meta:{label:`Eventing`,description:`Sets up the I/O Events and the Commerce events required by the application`},when:hasEventing,context:createEventsStepContext,children:[commerceEventsStep,externalEventsStep]});function createWebhookSubscriptions(context){let{logger}=context;return logger.info(`Creating webhooks in Commerce`),{subscriptionsCreated:!0}}function hasWebhooks(config){return`webhooks`in config&&Array.isArray(config.webhooks)&&config.webhooks.length,!1}const webhooksStep=defineBranchStep({name:`webhooks`,meta:{label:`Webhooks`,description:`Sets up Commerce webhooks`},when:hasWebhooks,children:[defineLeafStep({name:`subscriptions`,meta:{label:`Create Subscriptions`,description:`Creates webhook subscriptions in Adobe Commerce`},run:(config,context)=>{let{logger}=context;return logger.debug(config),createWebhookSubscriptions(context)}})]});function createDefaultChildSteps(config){return[eventingStep,webhooksStep,createCustomInstallationStep(config)]}function createRootInstallationStep(config){return defineBranchStep({name:`installation`,meta:{label:`Installation`,description:`App installation workflow`},children:createDefaultChildSteps(config)})}function createInitialInstallationState(options){let{config}=options;return createInitialState({rootStep:createRootInstallationStep(config),config})}function runInstallation(options){let{installationContext,config,initialState,hooks}=options;return executeWorkflow({rootStep:createRootInstallationStep(config),installationContext,config,initialState,hooks})}export{isFailedState as a,isCompletedState as i,runInstallation as n,isInProgressState as o,defineCustomInstallationStep as r,isSucceededState as s,createInitialInstallationState as t};
@@ -0,0 +1 @@
1
+ import{t as stringifyError}from"./error-hBHRgZ9R.mjs";import{a as stringValueSchema,i as nonEmptyStringValueSchema,n as alphaNumericOrUnderscoreSchema,o as titleCaseSchema,r as booleanValueSchema,t as alphaNumericOrHyphenSchema}from"./schemas-eemlD-xS.mjs";import{existsSync}from"fs";import{dirname,join,parse}from"path";import*as v from"valibot";import{CommerceSdkValidationError}from"@adobe/aio-commerce-lib-core/error";import{access,mkdir,readFile}from"fs/promises";import{createJiti}from"jiti";async function findUp(name,options={}){let names=Array.isArray(name)?name:[name],cwd=options.cwd||process.cwd(),{root}=parse(cwd),stopAt=options.stopAt||root,currentDir=cwd;for(;;){for(let fileName of names){let filePath=join(currentDir,fileName);try{return await access(filePath),filePath}catch{}}if(currentDir===stopAt||currentDir===root)return;currentDir=dirname(currentDir)}}async function findNearestPackageJson(cwd=process.cwd()){return await findUp(`package.json`,{cwd})||null}async function getProjectRootDirectory(cwd=process.cwd()){let packageJsonPath=await findNearestPackageJson(cwd);if(!packageJsonPath)throw Error("Could not find a the root directory of the project. `package.json` file not found.");return dirname(packageJsonPath)}async function makeOutputDirFor(fileOrFolder){let outputDir=join(await getProjectRootDirectory(),fileOrFolder);return existsSync(outputDir)||await mkdir(outputDir,{recursive:!0}),outputDir}async function detectPackageManager(cwd=process.cwd()){let rootDirectory=await getProjectRootDirectory(cwd),lockFileMap={"bun.lockb":`bun`,"pnpm-lock.yaml":`pnpm`,"yarn.lock":`yarn`,"package-lock.json":`npm`},lockFileName=Object.keys(lockFileMap).find(name=>existsSync(join(rootDirectory,name)));return lockFileName?lockFileMap[lockFileName]:`npm`}function getExecCommand(packageManager){return{pnpm:`pnpx`,yarn:`yarn dlx`,bun:`bunx`,npm:`npx`}[packageManager]}const BaseOptionSchema=v.object({name:v.pipe(v.string(`Expected a string for the field name`),v.nonEmpty(`The field name must not be empty`)),label:v.optional(v.string(`Expected a string for the field label`)),description:v.optional(v.string(`Expected a string for the field description`))}),ListOptionSchema=v.object({label:v.string(`Expected a string for the option label`),value:v.string(`Expected a string for the option value`)}),SingleListSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`list`,`Expected the type to be 'list'`),selectionMode:v.literal(`single`,`Expected the selectionMode to be 'single'`),options:v.array(ListOptionSchema,`Expected an array of list options`),default:v.pipe(v.string(`Expected a string for the default value`),v.nonEmpty(`The default value must not be empty`))}),MultipleListSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`list`,`Expected the type to be 'list'`),selectionMode:v.literal(`multiple`,`Expected the selectionMode to be 'multiple'`),options:v.array(ListOptionSchema,`Expected an array of list options`),default:v.optional(v.array(v.pipe(v.string(`Expected a string for each default value`),v.nonEmpty(`Each default value must not be empty`)),`Expected an array of default values`),[])}),ListSchema=v.variant(`selectionMode`,[SingleListSchema,MultipleListSchema]),TextSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`text`,`Expected the type to be 'text'`),default:v.optional(v.string(`Expected a string for the default value`))}),PasswordSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`password`,`Expected the type to be 'password'`),default:v.optional(v.string(`Expected a string for the default value`))}),EmailSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`email`,`Expected the type to be 'email'`),default:v.optional(v.pipe(v.string(`Expected a string for the default email value`),v.email(`The email must be a valid email address`)))}),UrlSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`url`,`Expected the type to be 'url'`),default:v.optional(v.pipe(v.string(`Expected a string for the default URL value`),v.url(`The URL must be a valid URL`)))}),PhoneSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`tel`,`Expected the type to be 'tel'`),default:v.optional(v.pipe(v.string(`Expected a string for the default phone number value`),v.regex(/^\+?[0-9\s\-()]+$/,`The phone number must contain only numbers and/or country codes`)))}),FieldSchema=v.variant(`type`,[ListSchema,TextSchema,PasswordSchema,EmailSchema,UrlSchema,PhoneSchema]),SchemaBusinessConfigSchema=v.pipe(v.array(FieldSchema,`Expected an array of configuration fields`),v.minLength(1,`At least one configuration parameter is required`)),SchemaBusinessConfig=v.object({schema:v.optional(SchemaBusinessConfigSchema,[])}),COMMERCE_EVENT_NAME_REGEX=/^(?:plugin|observer)\.[a-z_]+$/,FIELD_NAME_REGEX=/^([a-zA-Z0-9_\-.[\]]+|\*)$/;function commerceEventNameSchema(){return v.pipe(nonEmptyStringValueSchema(`event name`),v.regex(COMMERCE_EVENT_NAME_REGEX,`Event name must start with "plugin." or "observer." followed by lowercase letters and underscores only (e.g., "plugin.order_placed")`))}function fieldNameSchema(){return v.pipe(nonEmptyStringValueSchema(`field name`),v.regex(FIELD_NAME_REGEX,`Field name must contain only letters (a-z, A-Z), numbers (0-9), underscores (_), dashes (-), dots (.), and square brackets ([, ]), or be exactly "*"`))}function commerceEventFieldSchema(){return v.object({name:fieldNameSchema(),source:v.optional(stringValueSchema(`field source`))})}const ProviderSchema=v.object({label:v.pipe(titleCaseSchema(`provider label`),v.maxLength(100,`The provider label must not be longer than 100 characters`)),description:v.pipe(nonEmptyStringValueSchema(`provider description`),v.maxLength(255,`The provider description must not be longer than 255 characters`)),key:v.optional(v.pipe(alphaNumericOrHyphenSchema(`provider key`),v.maxLength(50,`The provider key must not be longer than 50 characters`)))}),BaseEventSchema=v.object({label:v.pipe(titleCaseSchema(`event label`),v.maxLength(100,`The event label must not be longer than 100 characters`)),description:v.pipe(nonEmptyStringValueSchema(`event description`),v.maxLength(255,`The event description must not be longer than 255 characters`)),runtimeActions:v.array(v.pipe(nonEmptyStringValueSchema(`runtime action`),v.regex(/^[a-z0-9-]+\/[a-z0-9-]+$/,`Runtime action must be in the format "<package>/<action>" (e.g., "my-package/my-action")`)),`Expected an array of runtime actions in the format <package>/<action>`)}),OPERATORS=[`greaterThan`,`lessThan`,`equal`,`regex`,`in`,`onChange`],ruleOperatorSchema=v.union(OPERATORS.map(op=>v.literal(op)),`Operator must be one of: ${OPERATORS.join(`, `)}`),CommerceEventRuleSchema=v.object({field:nonEmptyStringValueSchema(`rule field`),operator:ruleOperatorSchema,value:nonEmptyStringValueSchema(`rule value`)}),CommerceEventSchema=v.object({...BaseEventSchema.entries,name:commerceEventNameSchema(),fields:v.array(commerceEventFieldSchema(),`Expected an array of event field objects with a 'name' property`),rules:v.optional(v.array(CommerceEventRuleSchema,`Expected an array of event rules with field, operator, and value`)),destination:v.optional(nonEmptyStringValueSchema(`destination`)),hipaaAuditRequired:v.optional(booleanValueSchema(`hipaaAuditRequired`)),prioritary:v.optional(booleanValueSchema(`prioritary`)),force:v.optional(booleanValueSchema(`force`))}),ExternalEventSchema=v.object({...BaseEventSchema.entries,name:alphaNumericOrUnderscoreSchema(`event name`,`lowercase`)}),CommerceEventSourceSchema=v.object({provider:ProviderSchema,events:v.array(CommerceEventSchema,`Expected an array of Commerce events`)}),ExternalEventSourceSchema=v.object({provider:ProviderSchema,events:v.array(ExternalEventSchema,`Expected an array of external events`)}),EventingSchema=v.object({commerce:v.optional(v.array(CommerceEventSourceSchema,`Expected an array of Commerce event sources`)),external:v.optional(v.array(ExternalEventSourceSchema,`Expected an array of external event sources`))}),MAX_MESSAGE_LENGTH=1e3,CustomInstallationStepSchema=v.object({script:v.pipe(nonEmptyStringValueSchema(`script path`),v.regex(/^(?:\.{0,2}\/)*[\w-/]*[\w-]+\.js$/,`Script path must end with .js (e.g., "./setup.js", "./scripts/setup.js", or "../../scripts/setup.js")`)),name:v.pipe(nonEmptyStringValueSchema(`step name`),v.maxLength(255,`The step name must not be longer than 255 characters`)),description:v.pipe(nonEmptyStringValueSchema(`step description`),v.maxLength(255,`The step description must not be longer than 255 characters`))}),MessagesSchema=v.object({preInstallation:v.optional(v.pipe(nonEmptyStringValueSchema(`preInstallation message`),v.maxLength(MAX_MESSAGE_LENGTH,`The preInstallation message must not be longer than ${MAX_MESSAGE_LENGTH} characters`))),postInstallation:v.optional(v.pipe(nonEmptyStringValueSchema(`postInstallation message`),v.maxLength(MAX_MESSAGE_LENGTH,`The postInstallation message must not be longer than ${MAX_MESSAGE_LENGTH} characters`)))}),InstallationSchema=v.object({messages:v.optional(MessagesSchema),customInstallationSteps:v.optional(v.array(CustomInstallationStepSchema,`Expected an array of custom installation steps`))}),NUMERIC_IDENTIFIER=`(0|[1-9]\\d*)`,SEMVER_REGEX=RegExp(`^${NUMERIC_IDENTIFIER}\\.${NUMERIC_IDENTIFIER}\\.${NUMERIC_IDENTIFIER}$`);function nonEmptyString(fieldName){return v.pipe(v.string(`Expected a string for the ${fieldName}`),v.nonEmpty(`The ${fieldName} must not be empty`))}const MetadataSchema=v.object({id:alphaNumericOrHyphenSchema(`application id (metadata.id)`),displayName:v.pipe(nonEmptyString(`application display name`),v.maxLength(50,`The application display name must not be longer than 50 characters`)),description:v.pipe(nonEmptyString(`metadata description`),v.maxLength(255,`The metadata description must not be longer than 255 characters`)),version:v.pipe(nonEmptyString(`version`),v.regex(SEMVER_REGEX,`The version must follow semantic versioning (semver) format`))}),CommerceAppConfigSchemas={metadata:MetadataSchema,businessConfig:SchemaBusinessConfig,"businessConfig.schema":SchemaBusinessConfigSchema,eventing:EventingSchema,installation:InstallationSchema},CommerceAppConfigSchema=v.looseObject({metadata:MetadataSchema,businessConfig:v.optional(SchemaBusinessConfig),eventing:v.optional(EventingSchema),installation:v.optional(InstallationSchema)}),commerceAppConfigDomainsSchema=v.picklist(Object.keys(CommerceAppConfigSchemas));function validateCommerceAppConfig(config){let validatedConfig=v.safeParse(CommerceAppConfigSchema,config);if(!validatedConfig.success)throw new CommerceSdkValidationError(`Invalid commerce app config`,{issues:validatedConfig.issues});return validatedConfig.output}function validateCommerceAppConfigDomain(config,domain){let domainSchema=v.safeParse(commerceAppConfigDomainsSchema,domain);if(!domainSchema.success)throw new CommerceSdkValidationError(`Invalid commerce app config domain`,{issues:domainSchema.issues});let domainConfigSchema=CommerceAppConfigSchemas[domain],validatedConfig=v.safeParse(domainConfigSchema,config);if(!validatedConfig.success)throw new CommerceSdkValidationError(`Invalid commerce app config: ${domain}`,{issues:validatedConfig.issues});return validatedConfig.output}const jiti=createJiti(import.meta.url),configPaths=Object.freeze([`app.commerce.config.js`,`app.commerce.config.ts`,`app.commerce.config.cjs`,`app.commerce.config.mjs`,`app.commerce.config.mts`,`app.commerce.config.cts`]);async function resolveCommerceAppConfig(cwd=process.cwd()){let packageJsonPath=await findNearestPackageJson(cwd);if(!packageJsonPath)return null;let rootDirectory=dirname(packageJsonPath);for(let configPath of configPaths){let configFilePath=await findUp(configPath,{cwd,stopAt:rootDirectory});if(configFilePath)return configFilePath}return null}async function readCommerceAppConfig(cwd=process.cwd()){let configFilePath=await resolveCommerceAppConfig(cwd);if(!configFilePath)throw Error(`Could not find a commerce app config file in the current working directory or its parents.`);let config=null;try{config=await jiti.import(configFilePath)}catch(error){let message=stringifyError(error);throw Error(`Failed to read commerce app config file at ${configFilePath}: ${message}`,{cause:error})}if(!(config&&`default`in config)||config.default===void 0||config.default===null||typeof config.default==`object`&&Object.keys(config.default).length===0)throw Error("Commerce app config file does not export a default export. Make sure you use `export default` or `module.exports = { /* your config */ }`");return config.default}async function parseCommerceAppConfig(cwd=process.cwd()){return validateCommerceAppConfig(await readCommerceAppConfig(cwd))}export{validateCommerceAppConfigDomain as a,getProjectRootDirectory as c,validateCommerceAppConfig as i,makeOutputDirFor as l,readCommerceAppConfig as n,detectPackageManager as o,resolveCommerceAppConfig as r,getExecCommand as s,parseCommerceAppConfig as t};
@@ -0,0 +1 @@
1
+ import*as v from"valibot";const ALPHANUMERIC_OR_UNDERSCORE_REGEX={any:/^[a-zA-Z0-9_]+$/,lowercase:/^[a-z0-9_]+$/,uppercase:/^[A-Z0-9_]+$/},ALPHANUMERIC_OR_HYPHEN_REGEX={any:/^[a-zA-Z0-9-]+$/,lowercase:/^[a-z0-9-]+$/,uppercase:/^[A-Z0-9-]+$/},TITLE_CASE_REGEX=/^[A-Z][a-z]*(?:\s[A-Z][a-z]*)*$/;function stringValueSchema(name){return v.string(`Expected a string value for '${name}'`)}function nonEmptyStringValueSchema(name){return v.pipe(stringValueSchema(name),v.nonEmpty(`The value of "${name}" must not be empty`))}function booleanValueSchema(name){return v.boolean(`Expected a boolean value for '${name}'`)}function alphaNumericOrUnderscoreSchema(name,casing=`any`){let casingLabel=casing===`any`?``:` (${casing} only)`;return v.pipe(stringValueSchema(name),v.regex(ALPHANUMERIC_OR_UNDERSCORE_REGEX[casing],`Only alphanumeric characters and underscores are allowed in string value of "${name}"${casingLabel}`))}function alphaNumericOrHyphenSchema(name,casing=`any`){let casingLabel=casing===`any`?``:` (${casing} only)`;return v.pipe(stringValueSchema(name),v.regex(ALPHANUMERIC_OR_HYPHEN_REGEX[casing],`Only alphanumeric characters and hyphens are allowed in string value of "${name}"${casingLabel}`))}function titleCaseSchema(name){return v.pipe(nonEmptyStringValueSchema(name),v.regex(TITLE_CASE_REGEX,`The value of "${name}" must be in Title Case, which means, an uppercase letter after each space (e.g., "Some Title Case")`))}export{stringValueSchema as a,nonEmptyStringValueSchema as i,alphaNumericOrUnderscoreSchema as n,titleCaseSchema as o,booleanValueSchema as r,alphaNumericOrHyphenSchema as t};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@adobe/aio-commerce-lib-app",
3
3
  "type": "module",
4
4
  "author": "Adobe Inc.",
5
- "version": "0.1.0",
5
+ "version": "0.2.0",
6
6
  "private": false,
7
7
  "engines": {
8
8
  "node": ">=20 <=24"
@@ -21,6 +21,16 @@
21
21
  "@adobe/aio-commerce-lib-app": "./dist/es/commands/index.mjs"
22
22
  },
23
23
  "exports": {
24
+ "./actions": {
25
+ "import": {
26
+ "types": "./dist/es/actions/index.d.mts",
27
+ "default": "./dist/es/actions/index.mjs"
28
+ },
29
+ "require": {
30
+ "types": "./dist/cjs/actions/index.d.cts",
31
+ "default": "./dist/cjs/actions/index.cjs"
32
+ }
33
+ },
24
34
  "./config": {
25
35
  "import": {
26
36
  "types": "./dist/es/config/index.d.mts",
@@ -31,11 +41,22 @@
31
41
  "default": "./dist/cjs/config/index.cjs"
32
42
  }
33
43
  },
44
+ "./management": {
45
+ "import": {
46
+ "types": "./dist/es/management/index.d.mts",
47
+ "default": "./dist/es/management/index.mjs"
48
+ },
49
+ "require": {
50
+ "types": "./dist/cjs/management/index.d.cts",
51
+ "default": "./dist/cjs/management/index.cjs"
52
+ }
53
+ },
34
54
  "./package.json": "./package.json"
35
55
  },
36
56
  "imports": {
37
57
  "#*": "./source/*.ts",
38
- "#test/*": "./test/*.ts"
58
+ "#test/*": "./test/*.ts",
59
+ "#templates/*": "./source/commands/generate/actions/templates/*.template"
39
60
  },
40
61
  "files": [
41
62
  "dist",
@@ -44,21 +65,28 @@
44
65
  "README.md"
45
66
  ],
46
67
  "dependencies": {
47
- "@adobe/aio-commerce-lib-core": "^0.5.1",
48
68
  "consola": "^3.4.2",
49
69
  "jiti": "^2.6.1",
50
- "valibot": "^1.1.0"
70
+ "openwhisk": "^3.21.8",
71
+ "valibot": "^1.1.0",
72
+ "@adobe/aio-commerce-lib-api": "0.6.1",
73
+ "@adobe/aio-commerce-lib-auth": "0.8.1",
74
+ "@adobe/aio-commerce-lib-core": "0.6.1",
75
+ "@adobe/aio-commerce-lib-events": "0.6.0"
51
76
  },
52
77
  "devDependencies": {
78
+ "@adobe/aio-lib-core-logging": "^3.0.2",
79
+ "@aio-commerce-sdk/common-utils": "0.2.0",
53
80
  "@aio-commerce-sdk/config-tsdown": "1.0.0",
54
81
  "@aio-commerce-sdk/config-typedoc": "1.0.0",
55
82
  "@aio-commerce-sdk/config-typescript": "1.0.0",
56
- "@aio-commerce-sdk/scripting-utils": "0.1.1",
57
- "@aio-commerce-sdk/config-vitest": "1.0.0"
83
+ "@aio-commerce-sdk/config-vitest": "1.0.0",
84
+ "@aio-commerce-sdk/scripting-utils": "0.2.1"
58
85
  },
59
86
  "sideEffects": false,
60
87
  "scripts": {
61
88
  "build": "tsdown",
89
+ "pack": "pnpm pack",
62
90
  "publint": "publint",
63
91
  "docs": "typedoc && prettier --write '**/*.md'",
64
92
  "assist": "biome check --formatter-enabled=false --linter-enabled=false --assist-enabled=true --no-errors-on-unmatched",
@@ -1,14 +0,0 @@
1
- /**
2
- * @license
3
- *
4
- * Copyright 2025 Adobe. All rights reserved.
5
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License. You may obtain a copy
7
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
8
- *
9
- * Unless required by applicable law or agreed to in writing, software distributed under
10
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
11
- * OF ANY KIND, either express or implied. See the License for the specific language
12
- * governing permissions and limitations under the License.
13
- */
14
- var __create=Object.create,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty,__commonJSMin=(cb,mod)=>()=>(mod||cb((mod={exports:{}}).exports,mod),mod.exports),__copyProps=(to,from,except,desc)=>{if(from&&typeof from==`object`||typeof from==`function`)for(var keys=__getOwnPropNames(from),i=0,n=keys.length,key;i<n;i++)key=keys[i],!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:(k=>from[k]).bind(null,key),enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toESM=(mod,isNodeMode,target)=>(target=mod==null?{}:__create(__getProtoOf(mod)),__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,`default`,{value:mod,enumerable:!0}):target,mod));let fs_promises=require(`fs/promises`),path=require(`path`),__adobe_aio_commerce_lib_core_error=require(`@adobe/aio-commerce-lib-core/error`),fs=require(`fs`),jiti=require(`jiti`),valibot=require(`valibot`);valibot=__toESM(valibot);function stringifyError(error){return error instanceof __adobe_aio_commerce_lib_core_error.CommerceSdkValidationError?error.display():error instanceof Error?error.message:String(error)}async function findUp(name,options={}){let names=Array.isArray(name)?name:[name],cwd=options.cwd||process.cwd(),{root}=(0,path.parse)(cwd),stopAt=options.stopAt||root,currentDir=cwd;for(;;){for(let fileName of names){let filePath=(0,path.join)(currentDir,fileName);try{return await(0,fs_promises.access)(filePath),filePath}catch{}}if(currentDir===stopAt||currentDir===root)return;currentDir=(0,path.dirname)(currentDir)}}async function findNearestPackageJson(cwd=process.cwd()){return await findUp(`package.json`,{cwd})||null}async function getProjectRootDirectory(cwd=process.cwd()){let packageJsonPath=await findNearestPackageJson(cwd);if(!packageJsonPath)throw Error("Could not find a the root directory of the project. `package.json` file not found.");return(0,path.dirname)(packageJsonPath)}async function makeOutputDirFor(fileOrFolder){let outputDir=(0,path.join)(await getProjectRootDirectory(),fileOrFolder);return(0,fs.existsSync)(outputDir)||await(0,fs_promises.mkdir)(outputDir,{recursive:!0}),outputDir}async function detectPackageManager(cwd=process.cwd()){let rootDirectory=await getProjectRootDirectory(cwd),lockFileMap={"bun.lockb":`bun`,"pnpm-lock.yaml":`pnpm`,"yarn.lock":`yarn`,"package-lock.json":`npm`},lockFileName=Object.keys(lockFileMap).find(name=>(0,fs.existsSync)((0,path.join)(rootDirectory,name)));return lockFileName?lockFileMap[lockFileName]:`npm`}function getExecCommand(packageManager){return{pnpm:`pnpx`,yarn:`yarn dlx`,bun:`bunx`,npm:`npx`}[packageManager]}const BaseOptionSchema=valibot.object({name:valibot.pipe(valibot.string(`Expected a string for the field name`),valibot.nonEmpty(`The field name must not be empty`)),label:valibot.optional(valibot.string(`Expected a string for the field label`)),description:valibot.optional(valibot.string(`Expected a string for the field description`))}),ListOptionSchema=valibot.object({label:valibot.string(`Expected a string for the option label`),value:valibot.string(`Expected a string for the option value`)}),SingleListSchema=valibot.object({...BaseOptionSchema.entries,type:valibot.literal(`list`,`Expected the type to be 'list'`),selectionMode:valibot.literal(`single`,`Expected the selectionMode to be 'single'`),options:valibot.array(ListOptionSchema,`Expected an array of list options`),default:valibot.pipe(valibot.string(`Expected a string for the default value`),valibot.nonEmpty(`The default value must not be empty`))}),MultipleListSchema=valibot.object({...BaseOptionSchema.entries,type:valibot.literal(`list`,`Expected the type to be 'list'`),selectionMode:valibot.literal(`multiple`,`Expected the selectionMode to be 'multiple'`),options:valibot.array(ListOptionSchema,`Expected an array of list options`),default:valibot.optional(valibot.array(valibot.pipe(valibot.string(`Expected a string for each default value`),valibot.nonEmpty(`Each default value must not be empty`)),`Expected an array of default values`),[])}),ListSchema=valibot.variant(`selectionMode`,[SingleListSchema,MultipleListSchema]),TextSchema=valibot.object({...BaseOptionSchema.entries,type:valibot.literal(`text`,`Expected the type to be 'text'`),default:valibot.optional(valibot.string(`Expected a string for the default value`))}),PasswordSchema=valibot.object({...BaseOptionSchema.entries,type:valibot.literal(`password`,`Expected the type to be 'password'`),default:valibot.optional(valibot.string(`Expected a string for the default value`))}),EmailSchema=valibot.object({...BaseOptionSchema.entries,type:valibot.literal(`email`,`Expected the type to be 'email'`),default:valibot.optional(valibot.pipe(valibot.string(`Expected a string for the default email value`),valibot.email(`The email must be a valid email address`)))}),UrlSchema=valibot.object({...BaseOptionSchema.entries,type:valibot.literal(`url`,`Expected the type to be 'url'`),default:valibot.optional(valibot.pipe(valibot.string(`Expected a string for the default URL value`),valibot.url(`The URL must be a valid URL`)))}),PhoneSchema=valibot.object({...BaseOptionSchema.entries,type:valibot.literal(`tel`,`Expected the type to be 'tel'`),default:valibot.optional(valibot.pipe(valibot.string(`Expected a string for the default phone number value`),valibot.regex(/^\+?[0-9\s\-()]+$/,`The phone number must contain only numbers and/or country codes`)))}),FieldSchema=valibot.variant(`type`,[ListSchema,TextSchema,PasswordSchema,EmailSchema,UrlSchema,PhoneSchema]),SchemaBusinessConfigSchema=valibot.pipe(valibot.array(FieldSchema,`Expected an array of configuration fields`),valibot.minLength(1,`At least one configuration parameter is required`)),SchemaBusinessConfig=valibot.object({schema:valibot.optional(SchemaBusinessConfigSchema,[])}),MAX_DESCRIPTION_LENGTH=255,MAX_DISPLAY_NAME_LENGTH=50,NUMERIC_IDENTIFIER=`(0|[1-9]\\d*)`,SEMVER_REGEX=RegExp(`^${NUMERIC_IDENTIFIER}\\.${NUMERIC_IDENTIFIER}\\.${NUMERIC_IDENTIFIER}$`);function nonEmptyString(fieldName){return valibot.pipe(valibot.string(`Expected a string for the ${fieldName}`),valibot.nonEmpty(`The ${fieldName} must not be empty`))}const MetadataSchema=valibot.object({id:valibot.pipe(nonEmptyString(`application id`),valibot.regex(/^[a-zA-Z0-9-]+$/,`The application id must contain only alphanumeric characters and dashes`)),displayName:valibot.pipe(nonEmptyString(`application display name`),valibot.maxLength(50,`The application display name must not be longer than 50 characters`)),description:valibot.pipe(nonEmptyString(`metadata description`),valibot.maxLength(255,`The metadata description must not be longer than 255 characters`)),version:valibot.pipe(nonEmptyString(`version`),valibot.regex(SEMVER_REGEX,`The version must follow semantic versioning (semver) format`))}),CommerceAppConfigSchemas={metadata:MetadataSchema,businessConfig:SchemaBusinessConfig,"businessConfig.schema":SchemaBusinessConfigSchema},CommerceAppConfigSchema=valibot.object({metadata:MetadataSchema,businessConfig:valibot.optional(SchemaBusinessConfig)}),commerceAppConfigDomainsSchema=valibot.picklist(Object.keys(CommerceAppConfigSchemas));function validateCommerceAppConfig(config){let validatedConfig=valibot.safeParse(CommerceAppConfigSchema,config);if(!validatedConfig.success)throw new __adobe_aio_commerce_lib_core_error.CommerceSdkValidationError(`Invalid commerce app config`,{issues:validatedConfig.issues});return validatedConfig.output}function validateCommerceAppConfigDomain(config,domain){let domainSchema=valibot.safeParse(commerceAppConfigDomainsSchema,domain);if(!domainSchema.success)throw new __adobe_aio_commerce_lib_core_error.CommerceSdkValidationError(`Invalid commerce app config domain`,{issues:domainSchema.issues});let domainConfigSchema=CommerceAppConfigSchemas[domain],validatedConfig=valibot.safeParse(domainConfigSchema,config);if(!validatedConfig.success)throw new __adobe_aio_commerce_lib_core_error.CommerceSdkValidationError(`Invalid commerce app config: ${domain}`,{issues:validatedConfig.issues});return validatedConfig.output}const jiti$1=(0,jiti.createJiti)(require(`url`).pathToFileURL(__filename).href),BUNDLED_APP_COMMERCE_CONFIG_PATH=`app-management/app.commerce.manifest.json`,configPaths=Object.freeze([`app.commerce.config.js`,`app.commerce.config.ts`,`app.commerce.config.cjs`,`app.commerce.config.mjs`,`app.commerce.config.mts`,`app.commerce.config.cts`]);async function resolveCommerceAppConfig(cwd=process.cwd()){let packageJsonPath=await findNearestPackageJson(cwd);if(!packageJsonPath)return null;let rootDirectory=(0,path.dirname)(packageJsonPath);for(let configPath of configPaths){let configFilePath=await findUp(configPath,{cwd,stopAt:rootDirectory});if(configFilePath)return configFilePath}return null}async function readCommerceAppConfig(cwd=process.cwd()){let configFilePath=await resolveCommerceAppConfig(cwd);if(!configFilePath)throw Error(`Could not find a commerce app config file in the current working directory or its parents.`);let config=null;try{config=await jiti$1.import(configFilePath)}catch(error){let message=stringifyError(error);throw Error(`Failed to read commerce app config file at ${configFilePath}: ${message}`,{cause:error})}if(!(config&&`default`in config)||config.default===void 0||config.default===null||typeof config.default==`object`&&Object.keys(config.default).length===0)throw Error("Commerce app config file does not export a default export. Make sure you use `export default` or `module.exports = { /* your config */ }`");return config.default}async function parseCommerceAppConfig(cwd=process.cwd()){return validateCommerceAppConfig(await readCommerceAppConfig(cwd))}async function readBundledCommerceAppConfig(){try{let fileContents=await(0,fs_promises.readFile)(`app-management/app.commerce.manifest.json`,`utf-8`);return validateCommerceAppConfig(JSON.parse(fileContents))}catch(error){let message=stringifyError(error);throw Error(`Failed to read bundled commerce app config file: ${message}`,{cause:error})}}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return validateCommerceAppConfig}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return getExecCommand}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return __commonJSMin}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return __toESM}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return resolveCommerceAppConfig}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return makeOutputDirFor}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return readBundledCommerceAppConfig}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return validateCommerceAppConfigDomain}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return readCommerceAppConfig}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return detectPackageManager}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return parseCommerceAppConfig}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return stringifyError}});
@@ -1,14 +0,0 @@
1
- /**
2
- * @license
3
- *
4
- * Copyright 2025 Adobe. All rights reserved.
5
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License. You may obtain a copy
7
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
8
- *
9
- * Unless required by applicable law or agreed to in writing, software distributed under
10
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
11
- * OF ANY KIND, either express or implied. See the License for the specific language
12
- * governing permissions and limitations under the License.
13
- */
14
- import{access,mkdir,readFile}from"fs/promises";import{dirname,join,parse}from"path";import{CommerceSdkValidationError}from"@adobe/aio-commerce-lib-core/error";import{existsSync}from"fs";import{createJiti}from"jiti";import*as v from"valibot";function stringifyError(error){return error instanceof CommerceSdkValidationError?error.display():error instanceof Error?error.message:String(error)}async function findUp(name,options={}){let names=Array.isArray(name)?name:[name],cwd=options.cwd||process.cwd(),{root}=parse(cwd),stopAt=options.stopAt||root,currentDir=cwd;for(;;){for(let fileName of names){let filePath=join(currentDir,fileName);try{return await access(filePath),filePath}catch{}}if(currentDir===stopAt||currentDir===root)return;currentDir=dirname(currentDir)}}async function findNearestPackageJson(cwd=process.cwd()){return await findUp(`package.json`,{cwd})||null}async function getProjectRootDirectory(cwd=process.cwd()){let packageJsonPath=await findNearestPackageJson(cwd);if(!packageJsonPath)throw Error("Could not find a the root directory of the project. `package.json` file not found.");return dirname(packageJsonPath)}async function makeOutputDirFor(fileOrFolder){let outputDir=join(await getProjectRootDirectory(),fileOrFolder);return existsSync(outputDir)||await mkdir(outputDir,{recursive:!0}),outputDir}async function detectPackageManager(cwd=process.cwd()){let rootDirectory=await getProjectRootDirectory(cwd),lockFileMap={"bun.lockb":`bun`,"pnpm-lock.yaml":`pnpm`,"yarn.lock":`yarn`,"package-lock.json":`npm`},lockFileName=Object.keys(lockFileMap).find(name=>existsSync(join(rootDirectory,name)));return lockFileName?lockFileMap[lockFileName]:`npm`}function getExecCommand(packageManager){return{pnpm:`pnpx`,yarn:`yarn dlx`,bun:`bunx`,npm:`npx`}[packageManager]}const BaseOptionSchema=v.object({name:v.pipe(v.string(`Expected a string for the field name`),v.nonEmpty(`The field name must not be empty`)),label:v.optional(v.string(`Expected a string for the field label`)),description:v.optional(v.string(`Expected a string for the field description`))}),ListOptionSchema=v.object({label:v.string(`Expected a string for the option label`),value:v.string(`Expected a string for the option value`)}),SingleListSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`list`,`Expected the type to be 'list'`),selectionMode:v.literal(`single`,`Expected the selectionMode to be 'single'`),options:v.array(ListOptionSchema,`Expected an array of list options`),default:v.pipe(v.string(`Expected a string for the default value`),v.nonEmpty(`The default value must not be empty`))}),MultipleListSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`list`,`Expected the type to be 'list'`),selectionMode:v.literal(`multiple`,`Expected the selectionMode to be 'multiple'`),options:v.array(ListOptionSchema,`Expected an array of list options`),default:v.optional(v.array(v.pipe(v.string(`Expected a string for each default value`),v.nonEmpty(`Each default value must not be empty`)),`Expected an array of default values`),[])}),ListSchema=v.variant(`selectionMode`,[SingleListSchema,MultipleListSchema]),TextSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`text`,`Expected the type to be 'text'`),default:v.optional(v.string(`Expected a string for the default value`))}),PasswordSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`password`,`Expected the type to be 'password'`),default:v.optional(v.string(`Expected a string for the default value`))}),EmailSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`email`,`Expected the type to be 'email'`),default:v.optional(v.pipe(v.string(`Expected a string for the default email value`),v.email(`The email must be a valid email address`)))}),UrlSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`url`,`Expected the type to be 'url'`),default:v.optional(v.pipe(v.string(`Expected a string for the default URL value`),v.url(`The URL must be a valid URL`)))}),PhoneSchema=v.object({...BaseOptionSchema.entries,type:v.literal(`tel`,`Expected the type to be 'tel'`),default:v.optional(v.pipe(v.string(`Expected a string for the default phone number value`),v.regex(/^\+?[0-9\s\-()]+$/,`The phone number must contain only numbers and/or country codes`)))}),FieldSchema=v.variant(`type`,[ListSchema,TextSchema,PasswordSchema,EmailSchema,UrlSchema,PhoneSchema]),SchemaBusinessConfigSchema=v.pipe(v.array(FieldSchema,`Expected an array of configuration fields`),v.minLength(1,`At least one configuration parameter is required`)),SchemaBusinessConfig=v.object({schema:v.optional(SchemaBusinessConfigSchema,[])}),NUMERIC_IDENTIFIER=`(0|[1-9]\\d*)`,SEMVER_REGEX=RegExp(`^${NUMERIC_IDENTIFIER}\\.${NUMERIC_IDENTIFIER}\\.${NUMERIC_IDENTIFIER}$`);function nonEmptyString(fieldName){return v.pipe(v.string(`Expected a string for the ${fieldName}`),v.nonEmpty(`The ${fieldName} must not be empty`))}const MetadataSchema=v.object({id:v.pipe(nonEmptyString(`application id`),v.regex(/^[a-zA-Z0-9-]+$/,`The application id must contain only alphanumeric characters and dashes`)),displayName:v.pipe(nonEmptyString(`application display name`),v.maxLength(50,`The application display name must not be longer than 50 characters`)),description:v.pipe(nonEmptyString(`metadata description`),v.maxLength(255,`The metadata description must not be longer than 255 characters`)),version:v.pipe(nonEmptyString(`version`),v.regex(SEMVER_REGEX,`The version must follow semantic versioning (semver) format`))}),CommerceAppConfigSchemas={metadata:MetadataSchema,businessConfig:SchemaBusinessConfig,"businessConfig.schema":SchemaBusinessConfigSchema},CommerceAppConfigSchema=v.object({metadata:MetadataSchema,businessConfig:v.optional(SchemaBusinessConfig)}),commerceAppConfigDomainsSchema=v.picklist(Object.keys(CommerceAppConfigSchemas));function validateCommerceAppConfig(config){let validatedConfig=v.safeParse(CommerceAppConfigSchema,config);if(!validatedConfig.success)throw new CommerceSdkValidationError(`Invalid commerce app config`,{issues:validatedConfig.issues});return validatedConfig.output}function validateCommerceAppConfigDomain(config,domain){let domainSchema=v.safeParse(commerceAppConfigDomainsSchema,domain);if(!domainSchema.success)throw new CommerceSdkValidationError(`Invalid commerce app config domain`,{issues:domainSchema.issues});let domainConfigSchema=CommerceAppConfigSchemas[domain],validatedConfig=v.safeParse(domainConfigSchema,config);if(!validatedConfig.success)throw new CommerceSdkValidationError(`Invalid commerce app config: ${domain}`,{issues:validatedConfig.issues});return validatedConfig.output}const jiti=createJiti(import.meta.url),configPaths=Object.freeze([`app.commerce.config.js`,`app.commerce.config.ts`,`app.commerce.config.cjs`,`app.commerce.config.mjs`,`app.commerce.config.mts`,`app.commerce.config.cts`]);async function resolveCommerceAppConfig(cwd=process.cwd()){let packageJsonPath=await findNearestPackageJson(cwd);if(!packageJsonPath)return null;let rootDirectory=dirname(packageJsonPath);for(let configPath of configPaths){let configFilePath=await findUp(configPath,{cwd,stopAt:rootDirectory});if(configFilePath)return configFilePath}return null}async function readCommerceAppConfig(cwd=process.cwd()){let configFilePath=await resolveCommerceAppConfig(cwd);if(!configFilePath)throw Error(`Could not find a commerce app config file in the current working directory or its parents.`);let config=null;try{config=await jiti.import(configFilePath)}catch(error){let message=stringifyError(error);throw Error(`Failed to read commerce app config file at ${configFilePath}: ${message}`,{cause:error})}if(!(config&&`default`in config)||config.default===void 0||config.default===null||typeof config.default==`object`&&Object.keys(config.default).length===0)throw Error("Commerce app config file does not export a default export. Make sure you use `export default` or `module.exports = { /* your config */ }`");return config.default}async function parseCommerceAppConfig(cwd=process.cwd()){return validateCommerceAppConfig(await readCommerceAppConfig(cwd))}async function readBundledCommerceAppConfig(){try{let fileContents=await readFile(`app-management/app.commerce.manifest.json`,`utf-8`);return validateCommerceAppConfig(JSON.parse(fileContents))}catch(error){let message=stringifyError(error);throw Error(`Failed to read bundled commerce app config file: ${message}`,{cause:error})}}export{validateCommerceAppConfig as a,getExecCommand as c,resolveCommerceAppConfig as i,makeOutputDirFor as l,readBundledCommerceAppConfig as n,validateCommerceAppConfigDomain as o,readCommerceAppConfig as r,detectPackageManager as s,parseCommerceAppConfig as t,stringifyError as u};