@adobe/aio-commerce-lib-app 2.0.0 → 2.0.1-alpha-20260923160203

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 (69) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +4 -0
  3. package/dist/cjs/actions/app-config/index.cjs +3 -3
  4. package/dist/cjs/actions/association/index.cjs +1 -1
  5. package/dist/cjs/actions/config/index.cjs +1 -1
  6. package/dist/cjs/actions/installation/index.cjs +4 -4
  7. package/dist/cjs/actions/scope-tree/index.cjs +4 -3
  8. package/dist/cjs/cli/index.cjs +393 -0
  9. package/dist/cjs/cli/index.d.cts +31 -0
  10. package/dist/cjs/commands/index.cjs +73 -1717
  11. package/dist/cjs/{common-VSsQEHDv.cjs → common-B3KrOG-h.cjs} +1 -1
  12. package/dist/cjs/config/index.node.cjs +3 -3
  13. package/dist/{es/logging-CXesop3u.mjs → cjs/constants-tQUzmimN.cjs} +6 -18
  14. package/dist/cjs/{index.node-vcOo1rt0.cjs → index.node-DwuJfAiP.cjs} +1 -1
  15. package/dist/cjs/{logging-Br3xfBNe.cjs → logging-QTsrSwPc.cjs} +0 -16
  16. package/dist/cjs/management/index.cjs +1 -1
  17. package/dist/cjs/{management-Bki3hlxL.cjs → management-DplOGozL.cjs} +11 -10
  18. package/dist/cjs/{validate-CwWYtRsS.cjs → validate-DkmBVWFX.cjs} +1 -1
  19. package/dist/cjs/web-src-CCzvhSf0.cjs +1515 -0
  20. package/dist/cjs/{webhooks-Bcq2Qy7q.cjs → webhooks-zXjrg32x.cjs} +57 -57
  21. package/dist/es/actions/app-config/index.mjs +3 -3
  22. package/dist/es/actions/association/index.mjs +1 -1
  23. package/dist/es/actions/config/index.mjs +1 -1
  24. package/dist/es/actions/installation/index.mjs +4 -4
  25. package/dist/es/actions/scope-tree/index.mjs +3 -2
  26. package/dist/es/cli/index.d.mts +31 -0
  27. package/dist/es/cli/index.mjs +381 -0
  28. package/dist/es/commands/index.mjs +29 -1666
  29. package/dist/es/{common-dy6NrZt8.mjs → common-BWUDGasu.mjs} +1 -1
  30. package/dist/es/config/index.node.mjs +3 -3
  31. package/dist/es/constants-CSQsZaDP.mjs +25 -0
  32. package/dist/es/{index.node-CW46zuxW.mjs → index.node-DPiidKGS.mjs} +1 -1
  33. package/dist/es/logging-DbJHXSKh.mjs +32 -0
  34. package/dist/es/management/index.mjs +1 -1
  35. package/dist/es/{management-DgwAhaaJ.mjs → management-YDkb15SJ.mjs} +6 -5
  36. package/dist/es/{validate-NzW1FQwL.mjs → validate-Pd7RIA_3.mjs} +1 -1
  37. package/dist/es/web-src-CM_09qwk.mjs +1345 -0
  38. package/dist/es/{webhooks-CHZ1SXxw.mjs → webhooks-BMBfTqwi.mjs} +58 -58
  39. package/package.json +11 -1
  40. /package/dist/cjs/{commands/templates → templates}/admin-ui/tsconfig.json +0 -0
  41. /package/dist/cjs/{commands/templates → templates}/admin-ui/web-src/.babelrc +0 -0
  42. /package/dist/cjs/{commands/templates → templates}/admin-ui/web-src/index.css +0 -0
  43. /package/dist/cjs/{commands/templates → templates}/admin-ui/web-src/index.html +0 -0
  44. /package/dist/cjs/{commands/templates → templates}/admin-ui/web-src/src/app.jsx +0 -0
  45. /package/dist/cjs/{commands/templates → templates}/admin-ui/web-src/src/components/welcome.jsx +0 -0
  46. /package/dist/cjs/{commands/templates → templates}/admin-ui/web-src/src/pages/main-page.jsx +0 -0
  47. /package/dist/cjs/{commands/templates → templates}/admin-ui/web-src-template.d.ts +0 -0
  48. /package/dist/cjs/{commands/templates → templates}/app-management/app-config.js.template +0 -0
  49. /package/dist/cjs/{commands/templates → templates}/app-management/association.js.template +0 -0
  50. /package/dist/cjs/{commands/templates → templates}/app-management/custom-scripts.js.template +0 -0
  51. /package/dist/cjs/{commands/templates → templates}/app-management/installation.js.template +0 -0
  52. /package/dist/cjs/{commands/templates → templates}/business-configuration/config.js.template +0 -0
  53. /package/dist/cjs/{commands/templates → templates}/business-configuration/scope-tree.js.template +0 -0
  54. /package/dist/cjs/{commands/templates → templates}/typescript/webpack-config.cjs +0 -0
  55. /package/dist/es/{commands/templates → templates}/admin-ui/tsconfig.json +0 -0
  56. /package/dist/es/{commands/templates → templates}/admin-ui/web-src/.babelrc +0 -0
  57. /package/dist/es/{commands/templates → templates}/admin-ui/web-src/index.css +0 -0
  58. /package/dist/es/{commands/templates → templates}/admin-ui/web-src/index.html +0 -0
  59. /package/dist/es/{commands/templates → templates}/admin-ui/web-src/src/app.jsx +0 -0
  60. /package/dist/es/{commands/templates → templates}/admin-ui/web-src/src/components/welcome.jsx +0 -0
  61. /package/dist/es/{commands/templates → templates}/admin-ui/web-src/src/pages/main-page.jsx +0 -0
  62. /package/dist/es/{commands/templates → templates}/admin-ui/web-src-template.d.ts +0 -0
  63. /package/dist/es/{commands/templates → templates}/app-management/app-config.js.template +0 -0
  64. /package/dist/es/{commands/templates → templates}/app-management/association.js.template +0 -0
  65. /package/dist/es/{commands/templates → templates}/app-management/custom-scripts.js.template +0 -0
  66. /package/dist/es/{commands/templates → templates}/app-management/installation.js.template +0 -0
  67. /package/dist/es/{commands/templates → templates}/business-configuration/config.js.template +0 -0
  68. /package/dist/es/{commands/templates → templates}/business-configuration/scope-tree.js.template +0 -0
  69. /package/dist/es/{commands/templates → templates}/typescript/webpack-config.cjs +0 -0
@@ -0,0 +1,1345 @@
1
+ /**
2
+ * @license
3
+ *
4
+ * Copyright 2026 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
+
15
+ import { _ as makeOutputDirFor, a as resolveCommerceAppConfig, c as getExecCommand, d as getPackageExecutionCommand, f as getProjectInstallCommand, g as loadPackageJson, l as getInstallCommand, m as getRunScriptCommand, n as isTypeScriptConfig, o as appendCommand, p as getProjectRootDirectory, r as parseCommerceAppConfig, s as detectPackageManager, t as hasNamedCommerceAppConfigExports, u as getPackageDependencyInstallPlan, v as mergePackageJsonDependencies } from "./index.node-DPiidKGS.mjs";
16
+ import { f as hasCustomInstallationSteps } from "./webhooks-BMBfTqwi.mjs";
17
+ import { u as hasBusinessConfigSchema } from "./validate-Pd7RIA_3.mjs";
18
+ import { hasDynamicSchema } from "@adobe/aio-commerce-lib-config";
19
+ import { CommerceSdkValidationError } from "@adobe/aio-commerce-lib-core/error";
20
+ import { stringify as stringify$1 } from "safe-stable-stringify";
21
+ import { constants, existsSync } from "fs";
22
+ import { access, copyFile, mkdir, readFile, readdir, rm, writeFile } from "fs/promises";
23
+ import { basename, dirname, extname, join, relative, sep } from "path";
24
+ import consola$1, { consola } from "consola";
25
+ import { fileURLToPath } from "url";
26
+ import { formatTree } from "consola/utils";
27
+ import { execSync, spawnSync } from "child_process";
28
+ import * as prettier from "prettier";
29
+ import { Document, YAMLMap, YAMLSeq, isMap, isPair, isSeq, parseDocument } from "yaml";
30
+
31
+ //#region source/commands/generate/actions/constants.ts
32
+ const __filename = fileURLToPath(import.meta.url);
33
+ const __dirname = dirname(__filename);
34
+ /** The path to the action templates directory, sibling to this file. */
35
+ const TEMPLATES_DIR = join(__dirname, "templates");
36
+ /** The list of Commerce variables that are required for the runtime actions. */
37
+ const COMMERCE_VARIABLES = [
38
+ "AIO_COMMERCE_AUTH_IMS_CLIENT_ID",
39
+ "AIO_COMMERCE_AUTH_IMS_CLIENT_SECRETS",
40
+ "AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_ID",
41
+ "AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_EMAIL",
42
+ "AIO_COMMERCE_AUTH_IMS_ORG_ID",
43
+ "AIO_COMMERCE_AUTH_IMS_SCOPES"
44
+ ];
45
+ /** The inputs for the generated runtime actions. */
46
+ const COMMERCE_ACTION_INPUTS = Object.fromEntries(COMMERCE_VARIABLES.map((variable) => [variable, `$${variable}`]));
47
+ const CUSTOM_IMPORTS_PLACEHOLDER = "// {{CUSTOM_SCRIPTS_IMPORTS}}";
48
+ const CUSTOM_SCRIPTS_MAP_PLACEHOLDER = "// {{CUSTOM_SCRIPTS_MAP}}";
49
+ const CUSTOM_SCRIPTS_LOADER_PLACEHOLDER = "// {{CUSTOM_SCRIPTS_LOADER}}";
50
+
51
+ //#endregion
52
+ //#region source/commands/constants.ts
53
+ /** The name of the package containing all the generated runtime actions */
54
+ const PACKAGE_NAME = "app-management";
55
+ /** The path to the directory containing the generated files */
56
+ const GENERATED_PATH = ".generated";
57
+ /** The folder name containing the runtime actions */
58
+ const ACTIONS_FOLDER = `actions/${PACKAGE_NAME}`;
59
+ /** The path to the directory containing the generated actions */
60
+ const GENERATED_ACTIONS_PATH = `${GENERATED_PATH}/${ACTIONS_FOLDER}`;
61
+ /** The path to the directory containing the generated App Builder hook files */
62
+ const GENERATED_HOOKS_PATH = `${GENERATED_PATH}/hooks`;
63
+ /** The name of the extension point for extensibility. */
64
+ const EXTENSIBILITY_EXTENSION_POINT_ID = "commerce/extensibility/1";
65
+ /** The name of the extension point for configuration. */
66
+ const CONFIGURATION_EXTENSION_POINT_ID = "commerce/configuration/1";
67
+ /** The name of the extension point for Admin UI. */
68
+ const BACKEND_UI_V2_EXTENSION_POINT_ID = "commerce/backend-ui/2";
69
+ /** The name of the configuration schema file */
70
+ const APP_MANIFEST_FILE = "app.commerce.manifest.json";
71
+ /** The name of the generated runtime-safe app config module (dynamic schemas only). */
72
+ const RUNTIME_APP_CONFIG_FILE = "app.commerce.config.js";
73
+ /** Package import alias for the generated runtime-safe app config module. */
74
+ const APP_CONFIG_IMPORT_ALIAS = "#app.commerce.config";
75
+ /** The name of the commerce app configuration file */
76
+ const COMMERCE_APP_CONFIG_FILE = "app.commerce.config";
77
+ /** TypeScript development dependencies shared by root and web-src scaffolding. */
78
+ const SHARED_TYPESCRIPT_DEV_DEPENDENCIES = [{
79
+ name: "@tsconfig/bases",
80
+ version: "^1.0.26"
81
+ }, {
82
+ name: "typescript",
83
+ version: "^6.0.3"
84
+ }];
85
+ /** Node.js type definitions required only by root Runtime action scaffolding. */
86
+ const TYPESCRIPT_NODE_DEV_DEPENDENCY = {
87
+ name: "@types/node",
88
+ version: "^24.12.0"
89
+ };
90
+ /** TypeScript loader required only by root Runtime action scaffolding. */
91
+ const TYPESCRIPT_WEBPACK_DEV_DEPENDENCY = {
92
+ name: "ts-loader",
93
+ version: "^9.6.2"
94
+ };
95
+ /** The name of the configuration schema file */
96
+ const CONFIG_SCHEMA_FILE_NAME = "configuration-schema.json";
97
+ /** The name of the project package file */
98
+ const PACKAGE_JSON_FILE = "package.json";
99
+ /** The name of the app configuration file */
100
+ const APP_CONFIG_FILE = "app.config.yaml";
101
+ /** The name of the install configuration file */
102
+ const INSTALL_YAML_FILE = "install.yaml";
103
+ /** Get the path to the extension point folder */
104
+ function getExtensionPointFolderPath(extensionPointId) {
105
+ return `src/${extensionPointId.replaceAll("/", "-")}`;
106
+ }
107
+
108
+ //#endregion
109
+ //#region source/commands/utils.ts
110
+ /** Format file content using prettier, inferring the parser from the file path. */
111
+ function prettierFormat(content, filepath) {
112
+ return prettier.format(content, {
113
+ arrowParens: "always",
114
+ bracketSameLine: true,
115
+ bracketSpacing: true,
116
+ filepath,
117
+ printWidth: 80,
118
+ quoteStyle: "double",
119
+ semi: true,
120
+ tabWidth: 2,
121
+ trailingComma: "all",
122
+ useTabs: false
123
+ });
124
+ }
125
+ /**
126
+ * Load the app commerce config.
127
+ * @param projectRoot - Resolved project root containing the app configuration.
128
+ */
129
+ async function loadAppManifest(projectRoot) {
130
+ const appConfig = await parseCommerceAppConfig(projectRoot);
131
+ consola$1.debug("Loaded app commerce config");
132
+ return appConfig;
133
+ }
134
+ /**
135
+ * Install the given dependencies.
136
+ * @param packageManager - The detected package manager.
137
+ * @param dependencies - Package specifiers to install; no-op when empty.
138
+ * @param cwd - Working directory for the install command.
139
+ * @param options - Install command options.
140
+ */
141
+ function runInstall(packageManager, dependencies, cwd = process.cwd(), options = {}) {
142
+ if (dependencies.length === 0) return;
143
+ const dependencyListString = dependencies.map((dependency) => ` - ${dependency}`).join("\n");
144
+ consola$1.start([`Installing the following dependencies with ${packageManager}:\n${dependencyListString}\n`, "This may take a few seconds...\n"].join("\n"));
145
+ const { command, args } = getInstallCommand(packageManager, dependencies, options);
146
+ const displayCommand = [command, ...args].join(" ");
147
+ const result = spawnSync(command, args, {
148
+ cwd,
149
+ stdio: "inherit"
150
+ });
151
+ if (result.error || result.status !== 0) throw new Error(`Failed to install dependencies automatically. Please install manually: ${displayCommand}`, { cause: result.error ?? /* @__PURE__ */ new Error(`Install exited with code ${result.status}`) });
152
+ consola$1.log("");
153
+ consola$1.success("Dependencies installed successfully");
154
+ }
155
+ /**
156
+ * Install dependencies declared in package.json.
157
+ * @param packageManager - The detected package manager.
158
+ * @param cwd - Working directory for the install command.
159
+ */
160
+ function runProjectInstall(packageManager, cwd = process.cwd()) {
161
+ consola$1.start([`Installing project dependencies with ${packageManager}...`, "This may take a few seconds...\n"].join("\n"));
162
+ const { command, args } = getProjectInstallCommand(packageManager);
163
+ const displayCommand = [command, ...args].join(" ");
164
+ const result = spawnSync(command, args, {
165
+ cwd,
166
+ stdio: "inherit"
167
+ });
168
+ if (result.error || result.status !== 0) throw new Error(`Failed to install dependencies automatically. Please install manually: ${displayCommand}`, { cause: result.error ?? /* @__PURE__ */ new Error(`Install exited with code ${result.status}`) });
169
+ consola$1.log("");
170
+ consola$1.success("Dependencies installed successfully");
171
+ }
172
+ /**
173
+ * Whether the app config contains anything that can't be JSON-serialized as-is
174
+ * and therefore requires generating a runtime-safe ESM config module.
175
+ */
176
+ function hasDynamicAppConfig(appConfig) {
177
+ const schema = appConfig.businessConfig?.schema;
178
+ return Array.isArray(schema) && hasDynamicSchema(schema);
179
+ }
180
+ /**
181
+ * Whether preserving the app config requires generating a JavaScript module.
182
+ * @param appConfig - The parsed app config.
183
+ * @param projectRoot - Resolved project root containing the source config.
184
+ */
185
+ async function requiresJavaScriptAppConfig(appConfig, projectRoot) {
186
+ return hasDynamicAppConfig(appConfig) || await hasNamedCommerceAppConfigExports(projectRoot);
187
+ }
188
+ /**
189
+ * Path to an extension point's `.generated` directory, relative to the project root.
190
+ * @param extensionPointId - The extension point ID, e.g. "commerce/extensibility/1"
191
+ */
192
+ function getGeneratedDir(extensionPointId) {
193
+ return join(getExtensionPointFolderPath(extensionPointId), GENERATED_PATH);
194
+ }
195
+ /**
196
+ * Path to an extension point's generated actions directory, relative to the project root.
197
+ * @param extensionPointId - The extension point ID, e.g. "commerce/extensibility/1"
198
+ */
199
+ function getActionsDir(extensionPointId) {
200
+ return join(getExtensionPointFolderPath(extensionPointId), GENERATED_ACTIONS_PATH);
201
+ }
202
+ /**
203
+ * Path to a specific generated action file, relative to the project root.
204
+ * @param extensionPointId - The extension point ID, e.g. "commerce/extensibility/1"
205
+ * @param actionName - The name of the action, e.g. "my-action"
206
+ */
207
+ function getActionPath(extensionPointId, actionName) {
208
+ return join(getActionsDir(extensionPointId), `${actionName}.js`);
209
+ }
210
+ /**
211
+ * Path to the generated file of an App Builder hook, relative to the project root.
212
+ * @param extensionPointId - The extension point ID, e.g. "commerce/extensibility/1"
213
+ * @param hookName - The name of the hook, e.g. "pre-app-build"
214
+ */
215
+ function getHookPath(extensionPointId, hookName) {
216
+ return `${getExtensionPointFolderPath(extensionPointId)}/${GENERATED_HOOKS_PATH}/${hookName}.cjs`;
217
+ }
218
+ /**
219
+ * Path to an extension point's `ext.config.yaml`, relative to the project root.
220
+ * @param extensionPointId - The extension point ID, e.g. "commerce/extensibility/1"
221
+ */
222
+ function getExtConfigPath(extensionPointId) {
223
+ return join(getExtensionPointFolderPath(extensionPointId), "ext.config.yaml");
224
+ }
225
+ /** Path to the generated app manifest JSON file, relative to the project root. */
226
+ function getManifestPath() {
227
+ return join(getGeneratedDir(EXTENSIBILITY_EXTENSION_POINT_ID), APP_MANIFEST_FILE);
228
+ }
229
+ /**
230
+ * Path to the generated runtime-safe app config ESM module, relative to the
231
+ * project root. Used when the business config schema contains dynamic options.
232
+ */
233
+ function getRuntimeAppConfigPath() {
234
+ return join(getGeneratedDir(EXTENSIBILITY_EXTENSION_POINT_ID), RUNTIME_APP_CONFIG_FILE);
235
+ }
236
+ /** Path to the generated configuration schema file, relative to the project root. */
237
+ function getSchemaPath() {
238
+ return join(getGeneratedDir(CONFIGURATION_EXTENSION_POINT_ID), CONFIG_SCHEMA_FILE_NAME);
239
+ }
240
+
241
+ //#endregion
242
+ //#region source/commands/generate/actions/config.ts
243
+ /**
244
+ * Creates a runtime action configuration.
245
+ * @param actionName - The name of the action.
246
+ * @param config - Action generation options.
247
+ * @param options - Optional configuration options.
248
+ */
249
+ function createActionDefinition(actionName, config = {}, options = {}) {
250
+ const def = {
251
+ ...options,
252
+ annotations: {
253
+ final: true,
254
+ "require-adobe-auth": true
255
+ },
256
+ function: `${GENERATED_ACTIONS_PATH}/${actionName}.js`,
257
+ runtime: "nodejs:24",
258
+ web: options.web ?? "yes"
259
+ };
260
+ if (config.requiresEncryptionKey) def.inputs = {
261
+ ...def.inputs,
262
+ AIO_COMMERCE_CONFIG_ENCRYPTION_KEY: "$AIO_COMMERCE_CONFIG_ENCRYPTION_KEY"
263
+ };
264
+ return def;
265
+ }
266
+ /**
267
+ * Gets the runtime actions to be generated from the ext.config.yaml configuration.
268
+ * @param extConfig - The ext.config.yaml configuration.
269
+ * @param dir - Directory containing the runtime action templates.
270
+ */
271
+ function getRuntimeActions(extConfig, dir) {
272
+ return Object.entries(extConfig.runtimeManifest?.packages?.["app-management"]?.actions ?? {}).map(([name]) => ({
273
+ name,
274
+ templateFile: join(dir, `${name}.js.template`)
275
+ }));
276
+ }
277
+ /**
278
+ * Builds the ext.config.yaml configuration for the extensibility extension.
279
+ * @param appConfig - Parsed app configuration.
280
+ */
281
+ function buildAppManagementExtConfig(appConfig) {
282
+ const extConfig = {
283
+ hooks: {
284
+ "post-app-deploy": getHookPath(EXTENSIBILITY_EXTENSION_POINT_ID, "post-app-deploy"),
285
+ "pre-app-build": getHookPath(EXTENSIBILITY_EXTENSION_POINT_ID, "pre-app-build")
286
+ },
287
+ operations: { workerProcess: [{
288
+ impl: `${PACKAGE_NAME}/app-config`,
289
+ type: "action"
290
+ }, {
291
+ impl: `${PACKAGE_NAME}/association`,
292
+ type: "action"
293
+ }] },
294
+ runtimeManifest: { packages: { [PACKAGE_NAME]: {
295
+ actions: {
296
+ "app-config": createActionDefinition("app-config"),
297
+ association: createActionDefinition("association")
298
+ },
299
+ license: "Apache-2.0"
300
+ } } }
301
+ };
302
+ const hasPasswordFieldsInSchema = hasBusinessConfigSchema(appConfig) && appConfig.businessConfig.schema.some((field) => field.type === "password");
303
+ extConfig.operations.workerProcess.push({
304
+ impl: `${PACKAGE_NAME}/installation`,
305
+ type: "action"
306
+ });
307
+ extConfig.runtimeManifest.packages[PACKAGE_NAME].actions.installation = createActionDefinition("installation", { requiresEncryptionKey: hasPasswordFieldsInSchema }, {
308
+ inputs: {
309
+ ...COMMERCE_ACTION_INPUTS,
310
+ LOG_LEVEL: "$LOG_LEVEL"
311
+ },
312
+ limits: { timeout: 6e5 }
313
+ });
314
+ return extConfig;
315
+ }
316
+ /** Builds the ext.config.yaml configuration for the business configuration extension. */
317
+ function buildBusinessConfigurationExtConfig() {
318
+ const actions = [{
319
+ name: "config",
320
+ requiresEncryptionKey: true,
321
+ templateFile: "config.js.template"
322
+ }, {
323
+ name: "scope-tree",
324
+ templateFile: "scope-tree.js.template"
325
+ }];
326
+ return {
327
+ hooks: { "pre-app-build": getHookPath(CONFIGURATION_EXTENSION_POINT_ID, "pre-app-build") },
328
+ operations: { workerProcess: actions.map((action) => ({
329
+ impl: `${PACKAGE_NAME}/${action.name}`,
330
+ type: "action"
331
+ })) },
332
+ runtimeManifest: { packages: { [PACKAGE_NAME]: {
333
+ actions: Object.fromEntries(actions.map((action) => [action.name, createActionDefinition(action.name, action)])),
334
+ license: "Apache-2.0"
335
+ } } }
336
+ };
337
+ }
338
+ /** Collects unique `runtimeAction` strings from all worker entries in an `adminUi` config. */
339
+ function collectUniqueRuntimeActions(adminUi) {
340
+ const entities = [
341
+ "order",
342
+ "product",
343
+ "customer"
344
+ ].map((key) => adminUi?.[key]);
345
+ const gridRuntimeActions = entities.map((entity) => entity?.gridColumns?.runtimeAction).filter((action) => action !== void 0);
346
+ const massActionRuntimeActions = entities.flatMap((entity) => entity?.massActions ?? []).filter((action) => action.type === "worker").map((action) => action.runtimeAction);
347
+ const viewButtonRuntimeActions = (adminUi?.order?.viewButtons ?? []).filter((button) => button.type === "worker").map((button) => button.runtimeAction);
348
+ return [.../* @__PURE__ */ new Set([
349
+ ...gridRuntimeActions,
350
+ ...massActionRuntimeActions,
351
+ ...viewButtonRuntimeActions
352
+ ])];
353
+ }
354
+ /** Returns true if the `adminUi` config requires a `view` operation and `web` source in ext.config.yaml. */
355
+ function requiresWebSource(adminUi) {
356
+ if (adminUi?.menu !== void 0) return true;
357
+ if ((adminUi?.order?.viewButtons ?? []).some((button) => button.type === "view")) return true;
358
+ return [
359
+ "order",
360
+ "product",
361
+ "customer"
362
+ ].map((key) => adminUi?.[key]).flatMap((entity) => entity?.massActions ?? []).some((action) => action.type === "view");
363
+ }
364
+ /**
365
+ * Builds the ext.config.yaml for the Admin UI v2 extension (`commerce/backend-ui/2`).
366
+ * Derives `workerProcess` and `view` operation declarations from `adminUi` config.
367
+ * Adds a `view` operation and `web` source when view-type entries or `adminUi.menu` is configured.
368
+ * @param appConfig - Parsed app configuration.
369
+ */
370
+ function buildAdminUiV2ExtConfig(appConfig) {
371
+ const { adminUi } = appConfig;
372
+ const runtimeActions = collectUniqueRuntimeActions(adminUi);
373
+ const requiresWeb = requiresWebSource(adminUi);
374
+ return {
375
+ hooks: {
376
+ "pre-app-build": getHookPath(BACKEND_UI_V2_EXTENSION_POINT_ID, "pre-app-build"),
377
+ "pre-app-dev": getHookPath(BACKEND_UI_V2_EXTENSION_POINT_ID, "pre-app-dev"),
378
+ "pre-app-run": getHookPath(BACKEND_UI_V2_EXTENSION_POINT_ID, "pre-app-run")
379
+ },
380
+ operations: {
381
+ ...requiresWeb && { view: [{
382
+ impl: "index.html",
383
+ type: "web"
384
+ }] },
385
+ ...runtimeActions.length > 0 && { workerProcess: runtimeActions.map((impl) => ({
386
+ impl,
387
+ type: "action"
388
+ })) }
389
+ },
390
+ ...requiresWeb && { web: "web-src" }
391
+ };
392
+ }
393
+
394
+ //#endregion
395
+ //#region ../../packages-private/scripting-utils/source/yaml/helpers.ts
396
+ /**
397
+ * Read a YAML file and return a {@link Document}
398
+ * @param path - The path to the YAML file
399
+ */
400
+ async function readYamlFile(path) {
401
+ let doc = new Document();
402
+ if (existsSync(path)) try {
403
+ const fileContent = await readFile(path, "utf-8");
404
+ doc = parseDocument(fileContent, { keepSourceTokens: true });
405
+ } catch (err) {
406
+ const file = basename(path);
407
+ throw new Error(`Failed to parse ${file}`, { cause: err });
408
+ }
409
+ if (doc.contents === null) doc.contents = new YAMLMap();
410
+ return doc;
411
+ }
412
+ /**
413
+ * Generic helper to get or create a YAML node at the given path
414
+ * @param doc - The YAML document
415
+ * @param path - The path to the node
416
+ * @param options - The options for the node
417
+ * @param typeConfig - Configuration for the node type
418
+ * @returns The existing or newly created node
419
+ */
420
+ function getOrCreateNode(doc, path, options, typeConfig) {
421
+ const node = doc.getIn(path);
422
+ if (node) {
423
+ if (!typeConfig.typeGuard(node)) throw new Error(`Expected ${typeConfig.typeName} at path "${path.join(".")}".`);
424
+ return node;
425
+ }
426
+ if (doc.hasIn(path)) doc.deleteIn(path);
427
+ const pair = doc.createPair(path.at(-1), typeConfig.createNode());
428
+ options.onBeforeCreate?.(pair);
429
+ if (path.length === 1) doc.add(pair);
430
+ else doc.addIn(path.slice(0, -1), pair);
431
+ return doc.getIn(path);
432
+ }
433
+ /**
434
+ * Get or create a sequence at the given path
435
+ * @param doc - The YAML document
436
+ * @param path - The path to the sequence
437
+ * @param options - The options for the sequence
438
+ */
439
+ function getOrCreateSeq(doc, path, options) {
440
+ return getOrCreateNode(doc, path, options ?? {}, {
441
+ createNode: () => new YAMLSeq(),
442
+ typeGuard: isSeq,
443
+ typeName: "sequence"
444
+ });
445
+ }
446
+ /**
447
+ * Get or create a map at the given path
448
+ * @param doc - The YAML document
449
+ * @param path - The path to the map
450
+ * @param options - The options for the map
451
+ */
452
+ function getOrCreateMap(doc, path, options) {
453
+ return getOrCreateNode(doc, path, options ?? {}, {
454
+ createNode: () => new YAMLMap(),
455
+ typeGuard: isMap,
456
+ typeName: "map"
457
+ });
458
+ }
459
+ /**
460
+ * Read the `inputs` map from an existing YAML action definition into a plain
461
+ * object. Used by codegen to merge developer-added inputs with the ones the
462
+ * generator manages, so regeneration does not strip user-added entries.
463
+ *
464
+ * @param existingAction - The YAML map representing the existing action.
465
+ */
466
+ function getExistingInputs(existingAction) {
467
+ const inputs = existingAction?.get("inputs");
468
+ if (!isMap(inputs)) return {};
469
+ return Object.fromEntries(inputs.items.map((item) => {
470
+ if (!isPair(item)) return [];
471
+ return [String(item.key), item.value];
472
+ }));
473
+ }
474
+ /**
475
+ * Read a string field from an existing YAML action definition. Used by codegen
476
+ * to preserve developer-set values (e.g. `runtime`) across regeneration
477
+ * instead of overwriting them.
478
+ *
479
+ * @param key - The field to read.
480
+ * @param existingAction - The YAML map representing the existing action.
481
+ */
482
+ function getExistingString(key, existingAction) {
483
+ const value = existingAction?.get(key);
484
+ return typeof value === "string" ? value : void 0;
485
+ }
486
+
487
+ //#endregion
488
+ //#region ../../packages-private/scripting-utils/source/yaml/codegen.ts
489
+ /**
490
+ * Create an ext.config.yaml file
491
+ * @param path - The path to the ext.config.yaml file
492
+ * @param config - The config to build
493
+ * @param doc - The document to modify (a new one is created if not provided)
494
+ */
495
+ async function createOrUpdateExtConfig(path, config, doc) {
496
+ const extConfigDoc = doc ?? new Document({});
497
+ if (config.web !== void 0) buildWeb(extConfigDoc, config.web);
498
+ config.hooks ??= {};
499
+ buildHooks(extConfigDoc, config.hooks);
500
+ if (config.operations !== void 0) buildOperations(extConfigDoc, config.operations);
501
+ if (config.runtimeManifest !== void 0) buildRuntimeManifest(extConfigDoc, config.runtimeManifest);
502
+ await writeExtConfig(path, extConfigDoc);
503
+ return extConfigDoc;
504
+ }
505
+ /**
506
+ * Set the top-level `web` scalar only if it's missing.
507
+ * Preserves any user-customized value already present.
508
+ */
509
+ function buildWeb(extConfig, web) {
510
+ if (!extConfig.hasIn(["web"])) extConfig.add(extConfig.createPair("web", web));
511
+ }
512
+ /**
513
+ * Build the definition for a runtime action.
514
+ *
515
+ * Developer-added `inputs` keys (anything not in the generator-managed set) on
516
+ * `existingAction` are preserved so that hand-written entries — for example
517
+ * factory credentials for `dynamicList` fields — survive regeneration.
518
+ *
519
+ * A `runtime` already set on `existingAction` is likewise preserved, so a
520
+ * developer can pin a different Node runtime (e.g. `nodejs:24`) in
521
+ * `ext.config.yaml` once without codegen reverting it on the next run.
522
+ *
523
+ * @param action - The action definition to build.
524
+ * @param existingAction - The action's previous YAML definition, if any.
525
+ */
526
+ function buildActionDefinition(action, existingAction) {
527
+ const actionDef = new YAMLMap();
528
+ const existingInputs = getExistingInputs(existingAction);
529
+ const existingRuntime = getExistingString("runtime", existingAction);
530
+ const managedInputs = { LOG_LEVEL: "$LOG_LEVEL" };
531
+ actionDef.set("function", action.function);
532
+ actionDef.set("web", action.web ?? "yes");
533
+ actionDef.set("runtime", existingRuntime ?? action.runtime ?? "nodejs:24");
534
+ actionDef.set("inputs", {
535
+ ...existingInputs,
536
+ ...managedInputs,
537
+ ...action.inputs ?? {}
538
+ });
539
+ actionDef.set("annotations", { ...action.annotations ?? {
540
+ final: true,
541
+ "require-adobe-auth": true
542
+ } });
543
+ const includes = action.include ?? [];
544
+ if (includes.length > 0) {
545
+ const itemSeq = new YAMLSeq();
546
+ itemSeq.flow = true;
547
+ for (const [source, target] of includes) itemSeq.items.push(source, target);
548
+ const seq = new YAMLSeq();
549
+ seq.items.push(itemSeq);
550
+ actionDef.set("include", seq);
551
+ }
552
+ if (action.limits !== void 0) actionDef.set("limits", action.limits);
553
+ return actionDef;
554
+ }
555
+ /**
556
+ * Build the `operations` section of the `ext.config.yaml` file
557
+ * @param extConfig - The ext.config.yaml file
558
+ * @param operations - The operations to build
559
+ */
560
+ function buildOperations(extConfig, operations) {
561
+ const ourOps = operations.workerProcess ?? [];
562
+ if (ourOps.length > 0) {
563
+ getOrCreateMap(extConfig, ["operations"], { onBeforeCreate: (pair) => {
564
+ pair.key.spaceBefore = true;
565
+ } });
566
+ const workerProcess = getOrCreateSeq(extConfig, ["operations", "workerProcess"], { onBeforeCreate: (pair) => {
567
+ pair.key.commentBefore = " These worker processes definitions are auto-generated. Do not remove or manually edit.";
568
+ } });
569
+ workerProcess.items = [];
570
+ workerProcess.items.push(...ourOps.map((op) => {
571
+ const map = new YAMLMap();
572
+ map.set("type", op.type);
573
+ map.set("impl", op.impl);
574
+ return map;
575
+ }));
576
+ } else if (extConfig.hasIn(["operations", "workerProcess"])) extConfig.deleteIn(["operations", "workerProcess"]);
577
+ if (operations.view !== void 0) {
578
+ getOrCreateMap(extConfig, ["operations"], { onBeforeCreate: (pair) => {
579
+ pair.key.spaceBefore = true;
580
+ } });
581
+ const view = getOrCreateSeq(extConfig, ["operations", "view"]);
582
+ if (view.items.length === 0) view.items.push(...operations.view.map((v) => {
583
+ const map = new YAMLMap();
584
+ map.set("type", v.type);
585
+ map.set("impl", v.impl);
586
+ return map;
587
+ }));
588
+ }
589
+ const operationsMap = extConfig.getIn(["operations"]);
590
+ if (operationsMap instanceof YAMLMap && operationsMap.items.length === 0) extConfig.deleteIn(["operations"]);
591
+ }
592
+ /**
593
+ * Build the `runtimeManifest` section of the `ext.config.yaml` file
594
+ * @param extConfig - The ext.config.yaml file
595
+ * @param packages - The packages to build
596
+ */
597
+ function buildRuntimeManifest(extConfig, manifest) {
598
+ const packages = manifest.packages ?? {};
599
+ if (Object.keys(packages).length === 0) return;
600
+ getOrCreateMap(extConfig, ["runtimeManifest"], { onBeforeCreate: (pair) => {
601
+ pair.key.spaceBefore = true;
602
+ } });
603
+ getOrCreateMap(extConfig, ["runtimeManifest", "packages"]);
604
+ for (const [name, pkg] of Object.entries(packages)) {
605
+ const packageDef = getOrCreateMap(extConfig, [
606
+ "runtimeManifest",
607
+ "packages",
608
+ name
609
+ ], { onBeforeCreate: (pair) => {
610
+ pair.key.commentBefore = " This package definition is auto-generated. Do not remove or manually edit.";
611
+ } });
612
+ const existingActions = packageDef.get("actions");
613
+ const actions = new YAMLMap();
614
+ packageDef.set("license", pkg.license ?? "Apache-2.0");
615
+ packageDef.set("actions", actions);
616
+ for (const [actionName, action] of Object.entries(pkg.actions ?? {})) {
617
+ const existingAction = isMap(existingActions) ? existingActions.get(actionName) : void 0;
618
+ const actionDef = buildActionDefinition(action, isMap(existingAction) ? existingAction : void 0);
619
+ actions.set(actionName, actionDef);
620
+ }
621
+ }
622
+ }
623
+ /**
624
+ * Build the `hooks` section of the `ext.config.yaml` file. Each given hook
625
+ * replaces any previous value, while other hooks are left untouched.
626
+ * @param extConfig - The ext.config.yaml file
627
+ * @param hooks - The hooks to build, mapping hook names to JavaScript file paths
628
+ */
629
+ function buildHooks(extConfig, hooks) {
630
+ const generatedHooks = `[${Object.keys(hooks).join(", ")}]`;
631
+ const hooksMap = getOrCreateMap(extConfig, ["hooks"], { onBeforeCreate: (pair) => {
632
+ pair.key.spaceBefore = true;
633
+ pair.key.commentBefore = ` The ${generatedHooks} hooks are auto-generated. Do not remove or manually edit.`;
634
+ } });
635
+ for (const [name, path] of Object.entries(hooks)) hooksMap.set(name, path);
636
+ }
637
+ /**
638
+ * Write the ext.config.yaml file
639
+ * @param configPath - The path to the ext.config.yaml file
640
+ * @param config - The config to write
641
+ */
642
+ async function writeExtConfig(configPath, doc) {
643
+ const yamlContent = doc.toString({
644
+ defaultStringType: "PLAIN",
645
+ indent: 2,
646
+ lineWidth: 0
647
+ });
648
+ await writeFile(configPath, yamlContent, "utf-8");
649
+ }
650
+
651
+ //#endregion
652
+ //#region source/commands/generate/actions/lib.ts
653
+ const ESBUILD_PACKAGE = "esbuild@0.28.0";
654
+ const LEADING_DOT_SLASH_PATTERN$1 = /^\.\//u;
655
+ /** The `@adobe/aio-commerce-lib-app/cli` call exported by each generated hook file. */
656
+ const HOOK_HANDLER_CALLS = {
657
+ "post-app-deploy": () => "postAppDeploy()",
658
+ "pre-app-build": (extension) => `preAppBuild(${JSON.stringify(extension)})`,
659
+ "pre-app-dev": () => "preAppDev()",
660
+ "pre-app-run": () => "preAppRun()"
661
+ };
662
+ /** Normalize a path for use as an ESM import specifier. */
663
+ function normalizeImportPath(path) {
664
+ const normalizedPath = path.split(sep).join("/");
665
+ return normalizedPath.startsWith(".") ? normalizedPath : `./${normalizedPath}`;
666
+ }
667
+ /** Normalize a generated file path for package.json imports. */
668
+ function normalizePackageJsonPath$1(path) {
669
+ return normalizeImportPath(path).replace(LEADING_DOT_SLASH_PATTERN$1, "./");
670
+ }
671
+ /** Add or update the package import alias used by dynamic generated actions. */
672
+ async function updateAppConfigImportAlias(projectRoot, runtimeConfigPath) {
673
+ const pkg = await loadPackageJson(projectRoot);
674
+ if (pkg === null) throw new Error("Could not find package.json.");
675
+ const existingImports = typeof pkg.content.imports === "object" && pkg.content.imports !== null ? pkg.content.imports : {};
676
+ pkg.update({ imports: {
677
+ ...existingImports,
678
+ [APP_CONFIG_IMPORT_ALIAS]: normalizePackageJsonPath$1(runtimeConfigPath)
679
+ } });
680
+ await pkg.save();
681
+ }
682
+ /** Write an ESM passthrough module that re-exports an app config file. */
683
+ async function writeJavaScriptAppConfigModule(configFilePath, outputPath) {
684
+ const configImportPath = normalizeImportPath(relative(dirname(outputPath), configFilePath));
685
+ const isJson = extname(configFilePath) === ".json";
686
+ const importAttributes = isJson ? " with { type: \"json\" }" : "";
687
+ const defaultExport = isJson ? `export default /** @type {import("@adobe/aio-commerce-lib-app/config").CommerceAppConfig} */ (appConfig);` : "export default appConfig;";
688
+ await writeFile(outputPath, [
689
+ "// This file has been auto-generated by `@adobe/aio-commerce-lib-app`",
690
+ "// Do not modify this file directly",
691
+ "",
692
+ `import appConfig from "${configImportPath}"${importAttributes};`,
693
+ "",
694
+ isJson ? null : `export * from "${configImportPath}";`,
695
+ defaultExport,
696
+ ""
697
+ ].filter((item) => item !== null).join("\n"), "utf-8");
698
+ }
699
+ /**
700
+ * Bundles a TypeScript app config file into runtime-safe ESM.
701
+ * @param configFilePath - Source TypeScript app config path.
702
+ * @param outputPath - Generated ESM module path.
703
+ * @param projectRoot - Resolved project root used to run the bundler.
704
+ */
705
+ async function bundleTypeScriptAppConfigModule(configFilePath, outputPath, projectRoot) {
706
+ const packageManager = await detectPackageManager(projectRoot);
707
+ const { command, args } = getPackageExecutionCommand(packageManager, [
708
+ ESBUILD_PACKAGE,
709
+ configFilePath,
710
+ "--bundle",
711
+ "--format=esm",
712
+ "--log-level=silent",
713
+ `--outfile=${outputPath}`,
714
+ "--packages=external",
715
+ "--platform=node",
716
+ "--target=node22"
717
+ ], { allowBuild: "esbuild" });
718
+ const result = spawnSync(command, args, {
719
+ cwd: projectRoot,
720
+ stdio: "inherit"
721
+ });
722
+ if (result.error || result.status !== 0) throw new Error(`Could not bundle the TypeScript app config with ${ESBUILD_PACKAGE}.`, { cause: result.error ?? /* @__PURE__ */ new Error(`Bundle command exited with code ${result.status}`) });
723
+ }
724
+ /**
725
+ * Write an ESM module that re-exports the static app manifest JSON so generated
726
+ * actions can import it via the alias without needing a JSON import attribute.
727
+ * @param projectRoot - Resolved project root where the module is generated.
728
+ */
729
+ async function prepareStaticAppConfigImportAlias(projectRoot) {
730
+ const runtimeConfigPath = getRuntimeAppConfigPath();
731
+ const outputPath = join(projectRoot, runtimeConfigPath);
732
+ await makeOutputDirFor(dirname(runtimeConfigPath), projectRoot);
733
+ await writeJavaScriptAppConfigModule(join(projectRoot, getManifestPath()), outputPath);
734
+ }
735
+ /**
736
+ * Prepare the runtime config module consumed by generated actions and its
737
+ * `#app.commerce.config` package import alias.
738
+ *
739
+ * TypeScript configs are bundled into a generated runtime module. JavaScript
740
+ * configs use a passthrough module, while serializable default-only configs use
741
+ * a re-export of the validated JSON manifest.
742
+ * @param appManifest - App configuration used to select the module format.
743
+ * @param projectRoot - Resolved project root where the module is generated.
744
+ */
745
+ async function prepareRuntimeAppConfigModule(appManifest, projectRoot) {
746
+ const configFilePath = await resolveCommerceAppConfig(projectRoot);
747
+ if (await requiresJavaScriptAppConfig(appManifest, projectRoot)) {
748
+ if (configFilePath === null) throw new Error("Generating a runtime config module requires an app.commerce.config.* file.");
749
+ await makeOutputDirFor(dirname(getRuntimeAppConfigPath()), projectRoot);
750
+ const outputPath = join(projectRoot, getRuntimeAppConfigPath());
751
+ if (isTypeScriptConfig(configFilePath)) await bundleTypeScriptAppConfigModule(configFilePath, outputPath, projectRoot);
752
+ else await writeJavaScriptAppConfigModule(configFilePath, outputPath);
753
+ } else await prepareStaticAppConfigImportAlias(projectRoot);
754
+ await updateAppConfigImportAlias(projectRoot, getRuntimeAppConfigPath());
755
+ }
756
+ /**
757
+ * Reads an extension point's ext.config.yaml document and path.
758
+ * @param extensionPointId - The extension point ID to read the config for.
759
+ * @param projectRoot - Resolved project root containing the extension config.
760
+ */
761
+ async function readExtConfig(extensionPointId, projectRoot) {
762
+ const extConfigPath = join(projectRoot, getExtConfigPath(extensionPointId));
763
+ try {
764
+ return {
765
+ doc: await readYamlFile(extConfigPath),
766
+ path: extConfigPath
767
+ };
768
+ } catch (error) {
769
+ const reason = error instanceof Error ? error.message : "Unexpected error while reading the file.";
770
+ throw new Error([
771
+ `Could not read ext.config.yaml for ${extensionPointId} at ${extConfigPath}.`,
772
+ `Make sure the file exists and is valid YAML. You can regenerate it with "aio-commerce-lib-app generate actions".`,
773
+ `Reason: ${reason}`
774
+ ].join("\n"), { cause: error });
775
+ }
776
+ }
777
+ /**
778
+ * Updates an extension point's ext.config.yaml with generated configuration.
779
+ * @param appConfig - App configuration used to build the extension config.
780
+ * @param extensionPointId - Extension point whose config is updated.
781
+ * @param projectRoot - Resolved project root containing the extension config.
782
+ */
783
+ async function updateExtConfig(appConfig, extensionPointId, projectRoot) {
784
+ consola.info(`Updating ext.config.yaml for ${extensionPointId}...`);
785
+ await makeOutputDirFor(getExtensionPointFolderPath(extensionPointId), projectRoot);
786
+ const { path: extConfigPath, doc: extConfigDoc } = await readExtConfig(extensionPointId, projectRoot);
787
+ let extConfig;
788
+ switch (extensionPointId) {
789
+ case EXTENSIBILITY_EXTENSION_POINT_ID:
790
+ extConfig = buildAppManagementExtConfig(appConfig);
791
+ break;
792
+ case CONFIGURATION_EXTENSION_POINT_ID:
793
+ extConfig = buildBusinessConfigurationExtConfig();
794
+ break;
795
+ case BACKEND_UI_V2_EXTENSION_POINT_ID:
796
+ extConfig = buildAdminUiV2ExtConfig(appConfig);
797
+ break;
798
+ default: throw new Error(`Unsupported extension point: ${extensionPointId}`);
799
+ }
800
+ await createOrUpdateExtConfig(extConfigPath, extConfig, extConfigDoc);
801
+ await writeHookFiles(Object.keys(extConfig.hooks ?? {}), extensionPointId, projectRoot);
802
+ return extConfig;
803
+ }
804
+ /**
805
+ * Writes the CommonJS files that an extension point's `ext.config.yaml` hooks point to.
806
+ * @param hookNames - Names of the hooks registered for the extension point.
807
+ * @param extensionPointId - Extension point that owns the hooks.
808
+ * @param projectRoot - Resolved project root where the files are generated.
809
+ */
810
+ async function writeHookFiles(hookNames, extensionPointId, projectRoot) {
811
+ const extension = extensionPointId.replace("commerce/", "");
812
+ await Promise.all(hookNames.map(async (hookName) => {
813
+ const hookPath = getHookPath(extensionPointId, hookName);
814
+ const handlerCall = HOOK_HANDLER_CALLS[hookName](extension);
815
+ await makeOutputDirFor(dirname(hookPath), projectRoot);
816
+ await writeFile(join(projectRoot, hookPath), [
817
+ "// This file has been auto-generated by `@adobe/aio-commerce-lib-app`",
818
+ "// Do not modify this file directly",
819
+ "",
820
+ `module.exports = require("@adobe/aio-commerce-lib-app/cli").${handlerCall};`,
821
+ ""
822
+ ].join("\n"), "utf-8");
823
+ }));
824
+ }
825
+ /**
826
+ * Generates runtime action files from templates.
827
+ * @param appManifest - App configuration used to populate action templates.
828
+ * @param actions - Runtime actions to generate.
829
+ * @param extensionPointId - Extension point receiving the generated actions.
830
+ * @param templatesDir - Directory containing action templates.
831
+ * @param projectRoot - Resolved project root where actions are generated.
832
+ */
833
+ async function generateActionFiles(appManifest, actions, extensionPointId, templatesDir, projectRoot) {
834
+ consola.start("Generating runtime actions...");
835
+ await makeOutputDirFor(getActionsDir(extensionPointId), projectRoot);
836
+ const outputFiles = await Promise.all(actions.map(async (action) => {
837
+ const templatePath = join(templatesDir, action.templateFile);
838
+ let template = await readFile(templatePath, "utf-8");
839
+ if (action.name === "installation") {
840
+ const customScriptsTemplatePath = join(templatesDir, "app-management", "custom-scripts.js.template");
841
+ const scriptsTemplate = await generateCustomScriptsTemplate(await readFile(customScriptsTemplatePath, "utf-8"), appManifest, projectRoot);
842
+ template = applyCustomScripts(template, scriptsTemplate);
843
+ }
844
+ const actionPath = join(projectRoot, getActionPath(extensionPointId, action.name));
845
+ await writeFile(actionPath, template, "utf-8");
846
+ return ` ${relative(projectRoot, actionPath)}`;
847
+ }));
848
+ consola.success(`Generated ${actions.length} action(s)`);
849
+ consola.log.raw(formatTree(outputFiles));
850
+ }
851
+ /**
852
+ * Applies the given custom scripts template code to the given installation template.
853
+ * @param installationTemplate - The installation code runtime action template
854
+ * @param customScriptsTemplate - The custom scripts dynamically generated template.
855
+ */
856
+ function applyCustomScripts(installationTemplate, customScriptsTemplate) {
857
+ if (customScriptsTemplate !== null) return installationTemplate.replace(CUSTOM_SCRIPTS_LOADER_PLACEHOLDER, customScriptsTemplate).replace("const args = { appConfig };", "const args = { appConfig, customScriptsLoader };");
858
+ consola.debug("No custom installation steps found, skipping custom-scripts.js generation...");
859
+ return installationTemplate.replace(CUSTOM_SCRIPTS_LOADER_PLACEHOLDER, "// No custom installation scripts configured");
860
+ }
861
+ /**
862
+ * Generates installation code that imports configured custom scripts.
863
+ * @param template - Custom scripts template content.
864
+ * @param appManifest - App configuration containing custom installation steps.
865
+ * @param projectRoot - Resolved project root used to resolve script paths.
866
+ */
867
+ async function generateCustomScriptsTemplate(template, appManifest, projectRoot) {
868
+ if (!hasCustomInstallationSteps(appManifest)) return null;
869
+ const installationActionDir = join(projectRoot, getActionsDir(EXTENSIBILITY_EXTENSION_POINT_ID));
870
+ const customSteps = appManifest.installation.customInstallationSteps;
871
+ const importStatements = customSteps.map((step, index) => {
872
+ const absoluteScriptPath = join(projectRoot, step.script);
873
+ let relativeImportPath = relative(installationActionDir, absoluteScriptPath);
874
+ if (!relativeImportPath.startsWith(".")) relativeImportPath = `./${relativeImportPath}`;
875
+ relativeImportPath = relativeImportPath.replace(/\\/g, "/");
876
+ return `import * as ${`customScript${index}`} from "${relativeImportPath}";`;
877
+ }).join("\n");
878
+ const scriptMap = customSteps.map((step, index) => {
879
+ const entry = `"${step.script}": ${`customScript${index}`},`;
880
+ return entry.padStart(entry.length + 6);
881
+ }).join("\n");
882
+ return template.replace(CUSTOM_IMPORTS_PLACEHOLDER, importStatements).replace(CUSTOM_SCRIPTS_MAP_PLACEHOLDER, scriptMap);
883
+ }
884
+
885
+ //#endregion
886
+ //#region source/commands/generate/manifest/main.ts
887
+ /**
888
+ * Generates or removes the static app manifest as required by the app config.
889
+ * @param appConfig - Validated app configuration to serialize.
890
+ * @param projectRoot - Resolved project root where the manifest is generated.
891
+ */
892
+ async function run$1(appConfig, projectRoot) {
893
+ if (await requiresJavaScriptAppConfig(appConfig, projectRoot)) {
894
+ const stalePath = join(projectRoot, getManifestPath());
895
+ if (await access(stalePath).then(() => true, () => false)) {
896
+ await rm(stalePath, { force: true });
897
+ consola.success(`Removed stale ${APP_MANIFEST_FILE}`);
898
+ }
899
+ return;
900
+ }
901
+ consola.info("Generating app manifest...");
902
+ const contents = stringify$1(appConfig, null, 2);
903
+ const outputDir = await makeOutputDirFor(getGeneratedDir(EXTENSIBILITY_EXTENSION_POINT_ID), projectRoot);
904
+ const manifestPath = join(outputDir, APP_MANIFEST_FILE);
905
+ await writeFile(manifestPath, contents, "utf-8");
906
+ consola.success(`Generated ${APP_MANIFEST_FILE}\n`);
907
+ }
908
+ /** Run the generate manifest command */
909
+ async function exec$1() {
910
+ try {
911
+ const projectRoot = await getProjectRootDirectory();
912
+ await run$1(await loadAppManifest(projectRoot), projectRoot);
913
+ } catch (error) {
914
+ if (error instanceof CommerceSdkValidationError) consola.error(error.display());
915
+ else consola.error(error);
916
+ process.exit(1);
917
+ }
918
+ }
919
+
920
+ //#endregion
921
+ //#region ../../packages-private/scripting-utils/source/filesystem/helpers.ts
922
+ /**
923
+ * Touch a file, creating it if it doesn't exist.
924
+ * @param filePath The path of the file to touch
925
+ */
926
+ async function touch(filePath) {
927
+ await writeFile(filePath, "", { flag: "wx" }).catch((e) => {
928
+ if (e.code !== "EEXIST") throw e;
929
+ });
930
+ }
931
+
932
+ //#endregion
933
+ //#region source/commands/generate/schema/main.ts
934
+ /**
935
+ * Generates the business configuration schema and prepares encryption when needed.
936
+ * @param appConfig - Validated app configuration containing the schema.
937
+ * @param projectRoot - Resolved project root where schema files are managed.
938
+ */
939
+ async function run(appConfig, projectRoot) {
940
+ if (!hasBusinessConfigSchema(appConfig)) {
941
+ consola.debug("Business configuration schema not found in application configuration. Nothing to do.");
942
+ return;
943
+ }
944
+ const envPath = join(projectRoot, ".env");
945
+ await touch(envPath);
946
+ process.loadEnvFile(envPath);
947
+ if (appConfig.businessConfig.schema.some((field) => field.type === "password")) {
948
+ const packageExec = getExecCommand(await detectPackageManager(projectRoot));
949
+ if ("AIO_COMMERCE_CONFIG_ENCRYPTION_KEY" in process.env && String(process.env.AIO_COMMERCE_CONFIG_ENCRYPTION_KEY).trim().length > 0) execSync(`${packageExec} aio-commerce-lib-config encryption validate`);
950
+ else execSync(`${packageExec} aio-commerce-lib-config encryption setup`);
951
+ }
952
+ if (hasDynamicAppConfig(appConfig)) {
953
+ const stalePath = join(projectRoot, getSchemaPath());
954
+ if (await access(stalePath).then(() => true, () => false)) {
955
+ await rm(stalePath, { force: true });
956
+ consola.success(`Removed stale ${CONFIG_SCHEMA_FILE_NAME}`);
957
+ }
958
+ return;
959
+ }
960
+ consola.info("Generating configuration schema...");
961
+ const outputDir = await makeOutputDirFor(getGeneratedDir(CONFIGURATION_EXTENSION_POINT_ID), projectRoot);
962
+ const contents = stringify$1(appConfig.businessConfig.schema, null, 2);
963
+ const schemaPath = join(outputDir, CONFIG_SCHEMA_FILE_NAME);
964
+ await writeFile(schemaPath, contents, "utf-8");
965
+ consola.success(`Generated ${CONFIG_SCHEMA_FILE_NAME}\n`);
966
+ }
967
+ /** Run the generate schema command */
968
+ async function exec() {
969
+ try {
970
+ const projectRoot = await getProjectRootDirectory();
971
+ await run(await loadAppManifest(projectRoot), projectRoot);
972
+ } catch (error) {
973
+ if (error instanceof CommerceSdkValidationError) consola.error(error.display());
974
+ else consola.error(error);
975
+ process.exit(1);
976
+ }
977
+ }
978
+
979
+ //#endregion
980
+ //#region source/commands/typescript.ts
981
+ const WEBPACK_CONFIG_PATTERN = /^webpack-config\.(?:cjs|js)$/u;
982
+ const WEBPACK_CONFIG_FILE = "webpack-config.cjs";
983
+ const TSCONFIG_FILE = "tsconfig.json";
984
+ const ROOT_TYPESCRIPT_CONFIG = {
985
+ compilerOptions: {
986
+ allowJs: true,
987
+ checkJs: true,
988
+ module: "esnext",
989
+ moduleResolution: "bundler",
990
+ noEmit: true
991
+ },
992
+ exclude: ["src/**/web-src/**"],
993
+ extends: [
994
+ "@tsconfig/bases/recommended",
995
+ "@tsconfig/bases/node-ts",
996
+ "@tsconfig/bases/node24"
997
+ ],
998
+ include: [
999
+ "src/**/.generated/actions/**/*.js",
1000
+ "app.commerce.config.ts",
1001
+ "app.commerce.config.mts",
1002
+ "app.commerce.config.cts",
1003
+ "src/**/*.ts",
1004
+ "src/**/*.mts",
1005
+ "src/**/*.cts"
1006
+ ]
1007
+ };
1008
+ const TYPESCRIPT_DEV_DEPENDENCIES = [
1009
+ ...SHARED_TYPESCRIPT_DEV_DEPENDENCIES,
1010
+ TYPESCRIPT_NODE_DEV_DEPENDENCY,
1011
+ TYPESCRIPT_WEBPACK_DEV_DEPENDENCY
1012
+ ];
1013
+ const ACTIONS_TYPECHECK_SCRIPT = "typecheck:actions";
1014
+ const WEB_SOURCE_TYPECHECK_SCRIPT = "typecheck:web-src";
1015
+ const ACTIONS_TYPECHECK_COMMAND = "tsc --noEmit -p tsconfig.json";
1016
+ const WEB_SOURCE_TYPECHECK_COMMAND = `tsc --noEmit -p ${`${getExtensionPointFolderPath(BACKEND_UI_V2_EXTENSION_POINT_ID)}/web-src/tsconfig.json`}`;
1017
+ /**
1018
+ * Create the default Webpack configuration when the project has none.
1019
+ * @param projectRoot Project root directory.
1020
+ */
1021
+ async function scaffoldWebpackConfig(projectRoot) {
1022
+ const existingConfig = (await readdir(projectRoot, { withFileTypes: true })).find((entry) => entry.isFile() && WEBPACK_CONFIG_PATTERN.test(entry.name));
1023
+ if (existingConfig !== void 0) {
1024
+ consola.info(`Found ${existingConfig.name}; leaving it unchanged.`);
1025
+ return;
1026
+ }
1027
+ await copyFile(join(TEMPLATES_DIR, "typescript", WEBPACK_CONFIG_FILE), join(projectRoot, WEBPACK_CONFIG_FILE));
1028
+ }
1029
+ /**
1030
+ * Create the Runtime actions TypeScript configuration when it does not exist.
1031
+ * @param projectRoot Project root directory.
1032
+ */
1033
+ async function scaffoldTypeScriptConfig(projectRoot) {
1034
+ const rootConfigPath = join(projectRoot, TSCONFIG_FILE);
1035
+ if (!existsSync(rootConfigPath)) await writeFile(rootConfigPath, `${JSON.stringify(ROOT_TYPESCRIPT_CONFIG, null, 2)}\n`, "utf-8");
1036
+ }
1037
+ /**
1038
+ * Install missing TypeScript tooling after validating installed versions.
1039
+ * @param packageManager Package manager used by the project.
1040
+ * @param projectRoot Project root directory.
1041
+ */
1042
+ async function installTypeScriptDependencies(packageManager, projectRoot) {
1043
+ const installPlan = await getPackageDependencyInstallPlan(TYPESCRIPT_DEV_DEPENDENCIES, projectRoot);
1044
+ if (installPlan.incompatible.length > 0) {
1045
+ const dependencies = installPlan.incompatible.map(({ name, version, installedVersion }) => `${name}@${installedVersion} does not satisfy ${version}`).join("\n");
1046
+ throw new Error(`Cannot configure TypeScript because installed dependencies are incompatible:\n${dependencies}`);
1047
+ }
1048
+ runInstall(packageManager, installPlan.missing.map(({ name, version }) => `${name}@${version}`), projectRoot, { dev: true });
1049
+ }
1050
+ /**
1051
+ * Scaffold the files and dependencies for a new TypeScript Commerce project.
1052
+ * @param packageManager Package manager used by the initialized project.
1053
+ * @param projectRoot Resolved project root.
1054
+ */
1055
+ async function scaffoldTypeScriptProject(packageManager, projectRoot) {
1056
+ await scaffoldWebpackConfig(projectRoot);
1057
+ await scaffoldTypeScriptConfig(projectRoot);
1058
+ await installTypeScriptDependencies(packageManager, projectRoot);
1059
+ }
1060
+ /**
1061
+ * Add a generated typecheck script and compose it into the root typecheck script.
1062
+ * @param scriptName Generated package script name.
1063
+ * @param command TypeScript command run by the generated script.
1064
+ * @param projectRoot Resolved project root.
1065
+ */
1066
+ async function syncTypecheckScript(scriptName, command, projectRoot) {
1067
+ const pkg = await loadPackageJson(projectRoot);
1068
+ if (pkg === null) throw new Error("Could not find package.json.");
1069
+ const scripts = {
1070
+ ...pkg.content.scripts,
1071
+ [scriptName]: command
1072
+ };
1073
+ const packageManager = await detectPackageManager(projectRoot);
1074
+ const typecheck = appendCommand(scripts.typecheck, getRunScriptCommand(packageManager, scriptName));
1075
+ pkg.update({ scripts: {
1076
+ ...scripts,
1077
+ typecheck
1078
+ } });
1079
+ await pkg.save();
1080
+ }
1081
+ /**
1082
+ * Add the Runtime actions typecheck script to a project.
1083
+ * @param projectRoot Resolved project root.
1084
+ */
1085
+ async function syncActionsTypecheckScript(projectRoot) {
1086
+ await syncTypecheckScript(ACTIONS_TYPECHECK_SCRIPT, ACTIONS_TYPECHECK_COMMAND, projectRoot);
1087
+ }
1088
+ /**
1089
+ * Add the web-src typecheck script to a project.
1090
+ * @param projectRoot Resolved project root.
1091
+ */
1092
+ async function syncWebSourceTypecheckScript(projectRoot) {
1093
+ await syncTypecheckScript(WEB_SOURCE_TYPECHECK_SCRIPT, WEB_SOURCE_TYPECHECK_COMMAND, projectRoot);
1094
+ }
1095
+
1096
+ //#endregion
1097
+ //#region source/commands/generate/web-src.ts
1098
+ const JSX_FILE_EXTENSION = ".jsx";
1099
+ const TSX_FILE_EXTENSION = ".tsx";
1100
+ const APP_TITLE_PLACEHOLDER = "APP_TITLE";
1101
+ const WEB_SOURCE_ENTRYPOINT_FILE = "index.html";
1102
+ const WEB_SOURCE_IMPORT_ALIAS = "#web/*";
1103
+ const WEB_SOURCE_REQUIRED_FILES = [".babelrc"];
1104
+ const WEB_SOURCE_TSCONFIG_FILE = "tsconfig.json";
1105
+ const LEADING_DOT_SLASH_PATTERN = /^\.\//u;
1106
+ /** Runtime dependencies required by the generated web-src app. */
1107
+ const WEB_SOURCE_DEPENDENCIES = [
1108
+ {
1109
+ name: "@adobe/aio-commerce-lib-admin-ui",
1110
+ version: "^1.1.0"
1111
+ },
1112
+ {
1113
+ name: "react",
1114
+ version: "^19.2.7"
1115
+ },
1116
+ {
1117
+ name: "react-dom",
1118
+ version: "^19.2.7"
1119
+ },
1120
+ {
1121
+ name: "@react-spectrum/s2",
1122
+ version: "^1.5.1"
1123
+ }
1124
+ ];
1125
+ const WEB_SOURCE_BABEL_DEV_DEPENDENCIES = [{
1126
+ name: "@babel/core",
1127
+ version: "^7.29.7"
1128
+ }, {
1129
+ name: "@babel/preset-react",
1130
+ version: "^7.29.7"
1131
+ }];
1132
+ /** Development dependencies required by the generated web-src app. */
1133
+ const WEB_SOURCE_DEV_DEPENDENCIES = [
1134
+ ...WEB_SOURCE_BABEL_DEV_DEPENDENCIES,
1135
+ {
1136
+ name: "@types/react",
1137
+ version: "^19.2.17"
1138
+ },
1139
+ {
1140
+ name: "@types/react-dom",
1141
+ version: "^19.2.3"
1142
+ }
1143
+ ];
1144
+ const WEB_SOURCE_REQUIRED_DEV_DEPENDENCIES = WEB_SOURCE_BABEL_DEV_DEPENDENCIES;
1145
+ const WEB_SOURCE_SHARED_BUNDLES = [{
1146
+ assets: ["**/@react-spectrum/s2/**", "src/commerce-backend-ui-2/web-src/*.{js,jsx,ts,tsx}"],
1147
+ name: "s2-styles",
1148
+ types: ["css"]
1149
+ }];
1150
+ const WEB_SOURCE_TYPESCRIPT_CONFIG = {
1151
+ compilerOptions: {
1152
+ allowImportingTsExtensions: true,
1153
+ allowJs: true,
1154
+ checkJs: true,
1155
+ jsx: "react-jsx",
1156
+ jsxImportSource: "react",
1157
+ module: "esnext",
1158
+ moduleResolution: "bundler",
1159
+ noEmit: true,
1160
+ rewriteRelativeImportExtensions: false
1161
+ },
1162
+ extends: ["@tsconfig/bases/recommended"],
1163
+ include: [
1164
+ "src/**/*.js",
1165
+ "src/**/*.ts",
1166
+ "src/**/*.tsx",
1167
+ "src/**/*.jsx"
1168
+ ]
1169
+ };
1170
+ /** Normalize a generated file path for package.json imports. */
1171
+ function normalizePackageJsonPath(path) {
1172
+ const normalizedPath = path.split(sep).join("/");
1173
+ return (normalizedPath.startsWith(".") ? normalizedPath : `./${normalizedPath}`).replace(LEADING_DOT_SLASH_PATTERN, "./");
1174
+ }
1175
+ /**
1176
+ * Resolve the generated web-src entrypoint path from an extension config.
1177
+ * @param extConfig - Extension config containing the view operation.
1178
+ */
1179
+ function getWebSourceEntrypoint(extConfig) {
1180
+ const viewEntrypoint = extConfig.operations?.view?.[0]?.impl;
1181
+ if (viewEntrypoint === void 0) return null;
1182
+ return join(getExtensionPointFolderPath(BACKEND_UI_V2_EXTENSION_POINT_ID), extConfig.web ?? "web-src", viewEntrypoint);
1183
+ }
1184
+ /**
1185
+ * Ensure package.json has the dependencies and Parcel config needed by web-src.
1186
+ * @param projectRoot - Resolved project root containing package.json.
1187
+ * @param extension - Web source extension whose tooling is configured.
1188
+ */
1189
+ async function prepareWebSourcePackage(projectRoot, extension, scaffoldRequired) {
1190
+ const pkg = await loadPackageJson(projectRoot);
1191
+ if (pkg === null) throw new Error("Could not find package.json.");
1192
+ const dependencies = scaffoldRequired ? WEB_SOURCE_DEPENDENCIES : [];
1193
+ const requiredDevDependencies = scaffoldRequired ? [...WEB_SOURCE_DEV_DEPENDENCIES, ...extension === "tsx" ? SHARED_TYPESCRIPT_DEV_DEPENDENCIES : []] : WEB_SOURCE_REQUIRED_DEV_DEPENDENCIES;
1194
+ const installPlan = await getPackageDependencyInstallPlan([...dependencies, ...requiredDevDependencies], projectRoot);
1195
+ if (installPlan.incompatible.length > 0) {
1196
+ const incompatibleDependencies = installPlan.incompatible.map(({ name, version, installedVersion }) => `${name}@${installedVersion} does not satisfy ${version}`).join("\n");
1197
+ throw new Error(`Cannot scaffold web-src because installed dependencies are incompatible:\n${incompatibleDependencies}`);
1198
+ }
1199
+ const existingDependencies = pkg.content.dependencies ?? {};
1200
+ const devDependencies = pkg.content.devDependencies ?? {};
1201
+ const dependencyMaps = [existingDependencies, devDependencies];
1202
+ const declaredDependencyNames = new Set(dependencyMaps.flatMap((depMap) => Object.keys(depMap)));
1203
+ const dependenciesToDeclare = [...dependencies, ...requiredDevDependencies].filter(({ name }) => !declaredDependencyNames.has(name));
1204
+ if (dependenciesToDeclare.length > 0) {
1205
+ consola.info("Adding web-src dependencies in package.json:");
1206
+ consola.log.raw(formatTree(dependenciesToDeclare.map(({ name, version }) => ` ${name}@${version}`)));
1207
+ } else consola.info("web-src dependencies are already declared in package.json.");
1208
+ pkg.update({
1209
+ ...scaffoldRequired && {
1210
+ "@parcel/bundler-default": {
1211
+ ...pkg.content["@parcel/bundler-default"],
1212
+ manualSharedBundles: WEB_SOURCE_SHARED_BUNDLES
1213
+ },
1214
+ "@parcel/resolver-default": {
1215
+ ...pkg.content["@parcel/resolver-default"],
1216
+ packageExports: true
1217
+ },
1218
+ dependencies: mergePackageJsonDependencies(existingDependencies, WEB_SOURCE_DEPENDENCIES, dependencyMaps)
1219
+ },
1220
+ devDependencies: mergePackageJsonDependencies(devDependencies, requiredDevDependencies, dependencyMaps)
1221
+ });
1222
+ await pkg.save();
1223
+ if (installPlan.missing.length === 0) {
1224
+ consola.info("web-src dependencies are already installed.");
1225
+ return;
1226
+ }
1227
+ consola.info("Installing missing web-src dependencies from package.json:");
1228
+ consola.log.raw(formatTree(installPlan.missing.map(({ name, version }) => ` ${name}@${version}`)));
1229
+ const packageManager = await detectPackageManager(projectRoot);
1230
+ runProjectInstall(packageManager, projectRoot);
1231
+ }
1232
+ /**
1233
+ * Copies required web-src support files that are not already present.
1234
+ * @param sourceDir - Directory containing required web source files.
1235
+ * @param targetDir - Generated or existing web source directory.
1236
+ * @param projectRoot - Resolved project root used to format output paths.
1237
+ */
1238
+ async function copyMissingWebSourceFiles(sourceDir, targetDir, projectRoot) {
1239
+ return (await Promise.all(WEB_SOURCE_REQUIRED_FILES.map(async (file) => {
1240
+ const targetPath = join(targetDir, file);
1241
+ if (existsSync(targetPath)) return null;
1242
+ await copyFile(join(sourceDir, file), targetPath, constants.COPYFILE_EXCL);
1243
+ return ` ${relative(projectRoot, targetPath)}`;
1244
+ }))).filter((path) => path !== null);
1245
+ }
1246
+ /**
1247
+ * Add the package import alias for an existing or generated web-src.
1248
+ * @param extConfig - Extension config containing the view operation.
1249
+ * @param projectRoot - Resolved project root containing package.json.
1250
+ */
1251
+ async function prepareWebSourceImportAlias(extConfig, projectRoot) {
1252
+ const entrypoint = getWebSourceEntrypoint(extConfig);
1253
+ if (entrypoint === null) return;
1254
+ const pkg = await loadPackageJson(projectRoot);
1255
+ if (pkg === null) throw new Error("Could not find package.json.");
1256
+ const existingImports = typeof pkg.content.imports === "object" && pkg.content.imports !== null ? pkg.content.imports : {};
1257
+ const webSourceSrcPath = join(dirname(entrypoint), "src");
1258
+ pkg.update({ imports: {
1259
+ ...existingImports,
1260
+ [WEB_SOURCE_IMPORT_ALIAS]: `${normalizePackageJsonPath(webSourceSrcPath)}/*`
1261
+ } });
1262
+ await pkg.save();
1263
+ }
1264
+ /** Resolve the generated web-src file extension from the app config file type. */
1265
+ async function resolveWebSourceExtension(projectRoot) {
1266
+ const configFilePath = await resolveCommerceAppConfig(projectRoot);
1267
+ return configFilePath !== null && isTypeScriptConfig(configFilePath) ? "tsx" : "jsx";
1268
+ }
1269
+ async function writeWebSourceTypeScriptConfig(targetDir) {
1270
+ const tsconfigPath = join(targetDir, WEB_SOURCE_TSCONFIG_FILE);
1271
+ await writeFile(tsconfigPath, `${JSON.stringify(WEB_SOURCE_TYPESCRIPT_CONFIG, null, 2)}\n`, "utf-8");
1272
+ return tsconfigPath;
1273
+ }
1274
+ /**
1275
+ * Resolve the destination path for a web-src template file.
1276
+ * @param templatePath - Template file path relative to its source directory.
1277
+ * @param extension - Web source extension to generate.
1278
+ */
1279
+ function getWebSourceTemplateTargetPath(templatePath, extension) {
1280
+ if (extension === "jsx" || !templatePath.endsWith(JSX_FILE_EXTENSION)) return templatePath;
1281
+ return `${templatePath.slice(0, -4)}${TSX_FILE_EXTENSION}`;
1282
+ }
1283
+ /**
1284
+ * Copy web-src templates directly to the final file extension and entrypoint.
1285
+ * @param sourceDir - Template web-src directory.
1286
+ * @param targetDir - Generated web-src directory.
1287
+ * @param extension - Web source extension to generate.
1288
+ * @param appTitle - Application title used by the entrypoint template.
1289
+ * @param projectRoot - Resolved project root used to format output paths.
1290
+ */
1291
+ async function copyWebSourceTemplates(sourceDir, targetDir, extension, appTitle, projectRoot) {
1292
+ await mkdir(targetDir, { recursive: true });
1293
+ const entries = await readdir(sourceDir, { withFileTypes: true });
1294
+ return (await Promise.all(entries.map(async (entry) => {
1295
+ const sourcePath = join(sourceDir, entry.name);
1296
+ const targetPath = join(targetDir, getWebSourceTemplateTargetPath(entry.name, extension));
1297
+ if (entry.isDirectory()) return await copyWebSourceTemplates(sourcePath, targetPath, extension, appTitle, projectRoot);
1298
+ let content = await readFile(sourcePath, "utf-8");
1299
+ if (extension === "tsx") content = content.replaceAll(`${JSX_FILE_EXTENSION}"`, `${TSX_FILE_EXTENSION}"`);
1300
+ if (targetPath.endsWith(WEB_SOURCE_ENTRYPOINT_FILE)) content = content.replaceAll(APP_TITLE_PLACEHOLDER, appTitle);
1301
+ await writeFile(targetPath, content, {
1302
+ encoding: "utf-8",
1303
+ flag: "wx"
1304
+ });
1305
+ return ` ${relative(projectRoot, targetPath)}`;
1306
+ }))).flat();
1307
+ }
1308
+ /**
1309
+ * Generates the web source scaffold for an iframe-based Admin UI extension.
1310
+ * @param extConfig - Extension config containing the web entrypoint.
1311
+ * @param appName - Application name inserted into the generated entrypoint.
1312
+ * @param projectRoot - Resolved project root where web source is generated.
1313
+ * @param templatesDir - Directory containing web source templates.
1314
+ */
1315
+ async function generateWebSrc(extConfig, appName, projectRoot, templatesDir = TEMPLATES_DIR) {
1316
+ const entrypoint = getWebSourceEntrypoint(extConfig);
1317
+ if (entrypoint === null) return;
1318
+ const entrypointPath = join(projectRoot, entrypoint);
1319
+ const scaffoldRequired = !existsSync(entrypointPath);
1320
+ const targetDir = dirname(entrypointPath);
1321
+ const extension = await resolveWebSourceExtension(projectRoot);
1322
+ const sourceDir = join(templatesDir, "admin-ui", "web-src");
1323
+ let outputFiles = [];
1324
+ if (scaffoldRequired) {
1325
+ consola.start(`Scaffolding web-src for ${BACKEND_UI_V2_EXTENSION_POINT_ID}...`);
1326
+ outputFiles = await copyWebSourceTemplates(sourceDir, targetDir, extension, appName, projectRoot);
1327
+ if (extension === "tsx") {
1328
+ const tsconfigPath = await writeWebSourceTypeScriptConfig(targetDir);
1329
+ outputFiles.push(` ${relative(projectRoot, tsconfigPath)}`);
1330
+ await syncWebSourceTypecheckScript(projectRoot);
1331
+ }
1332
+ } else consola.info(`web-src entrypoint already exists, skipping scaffold: ${relative(projectRoot, entrypointPath)}`);
1333
+ const requiredFiles = await copyMissingWebSourceFiles(sourceDir, targetDir, projectRoot);
1334
+ if (scaffoldRequired || requiredFiles.length > 0) await prepareWebSourcePackage(projectRoot, extension, scaffoldRequired);
1335
+ if (scaffoldRequired) {
1336
+ consola.success(`Scaffolded ${relative(projectRoot, targetDir)}`);
1337
+ consola.log.raw(formatTree([...outputFiles, ...requiredFiles]));
1338
+ } else if (requiredFiles.length > 0) {
1339
+ consola.success(`Added required files to ${relative(projectRoot, targetDir)}`);
1340
+ consola.log.raw(formatTree(requiredFiles));
1341
+ }
1342
+ }
1343
+
1344
+ //#endregion
1345
+ export { COMMERCE_APP_CONFIG_FILE as C, PACKAGE_JSON_FILE as D, INSTALL_YAML_FILE as E, TEMPLATES_DIR as O, BACKEND_UI_V2_EXTENSION_POINT_ID as S, EXTENSIBILITY_EXTENSION_POINT_ID as T, getExtConfigPath as _, exec as a, runInstall as b, run$1 as c, readExtConfig as d, updateExtConfig as f, getRuntimeActions as g, readYamlFile as h, syncActionsTypecheckScript as i, generateActionFiles as l, getOrCreateSeq as m, prepareWebSourceImportAlias as n, run as o, getOrCreateMap as p, scaffoldTypeScriptProject as r, exec$1 as s, generateWebSrc as t, prepareRuntimeAppConfigModule as u, loadAppManifest as v, CONFIGURATION_EXTENSION_POINT_ID as w, APP_CONFIG_FILE as x, prettierFormat as y };