@salesforce/b2c-tooling-sdk 1.4.0 → 1.6.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.
- package/data/xsd/abtest.xsd +14 -1
- package/data/xsd/bmext.xsd +18 -0
- package/data/xsd/commercefeaturestate.xsd +101 -0
- package/data/xsd/index.json +10 -2
- package/data/xsd/library.xsd +33 -0
- package/data/xsd/order.xsd +2 -0
- package/data/xsd/pagemetatag.xsd +1 -0
- package/data/xsd/search2.xsd +39 -0
- package/data/xsd/sort.xsd +31 -1
- package/data/xsd/storefronts.xsd +104 -0
- package/dist/cjs/cli/lifecycle.d.ts +1 -1
- package/dist/cjs/cli/lifecycle.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/operations/cap/index.d.ts +56 -0
- package/dist/cjs/operations/cap/index.js +57 -0
- package/dist/cjs/operations/cap/index.js.map +1 -0
- package/dist/cjs/operations/cap/install.d.ts +53 -0
- package/dist/cjs/operations/cap/install.js +181 -0
- package/dist/cjs/operations/cap/install.js.map +1 -0
- package/dist/cjs/operations/cap/list.d.ts +92 -0
- package/dist/cjs/operations/cap/list.js +230 -0
- package/dist/cjs/operations/cap/list.js.map +1 -0
- package/dist/cjs/operations/cap/package.d.ts +39 -0
- package/dist/cjs/operations/cap/package.js +77 -0
- package/dist/cjs/operations/cap/package.js.map +1 -0
- package/dist/cjs/operations/cap/pull.d.ts +21 -0
- package/dist/cjs/operations/cap/pull.js +77 -0
- package/dist/cjs/operations/cap/pull.js.map +1 -0
- package/dist/cjs/operations/cap/uninstall.d.ts +46 -0
- package/dist/cjs/operations/cap/uninstall.js +87 -0
- package/dist/cjs/operations/cap/uninstall.js.map +1 -0
- package/dist/cjs/operations/cap/validate.d.ts +47 -0
- package/dist/cjs/operations/cap/validate.js +235 -0
- package/dist/cjs/operations/cap/validate.js.map +1 -0
- package/dist/cjs/operations/jobs/site-archive.d.ts +1 -0
- package/dist/cjs/operations/jobs/site-archive.js +30 -39
- package/dist/cjs/operations/jobs/site-archive.js.map +1 -1
- package/dist/cjs/operations/util/zip.d.ts +5 -0
- package/dist/cjs/operations/util/zip.js +25 -0
- package/dist/cjs/operations/util/zip.js.map +1 -0
- package/dist/cjs/plugins/loader.js +6 -1
- package/dist/cjs/plugins/loader.js.map +1 -1
- package/dist/cjs/skills/github.d.ts +2 -18
- package/dist/cjs/skills/github.js +195 -108
- package/dist/cjs/skills/github.js.map +1 -1
- package/dist/cjs/skills/index.d.ts +2 -1
- package/dist/cjs/skills/index.js +2 -0
- package/dist/cjs/skills/index.js.map +1 -1
- package/dist/cjs/skills/parser.d.ts +2 -2
- package/dist/cjs/skills/parser.js +13 -18
- package/dist/cjs/skills/parser.js.map +1 -1
- package/dist/cjs/skills/sources.d.ts +4 -0
- package/dist/cjs/skills/sources.js +44 -0
- package/dist/cjs/skills/sources.js.map +1 -0
- package/dist/cjs/skills/types.d.ts +16 -1
- package/dist/esm/cli/lifecycle.d.ts +1 -1
- package/dist/esm/cli/lifecycle.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/operations/cap/index.d.ts +56 -0
- package/dist/esm/operations/cap/index.js +57 -0
- package/dist/esm/operations/cap/index.js.map +1 -0
- package/dist/esm/operations/cap/install.d.ts +53 -0
- package/dist/esm/operations/cap/install.js +183 -0
- package/dist/esm/operations/cap/install.js.map +1 -0
- package/dist/esm/operations/cap/list.d.ts +92 -0
- package/dist/esm/operations/cap/list.js +231 -0
- package/dist/esm/operations/cap/list.js.map +1 -0
- package/dist/esm/operations/cap/package.d.ts +39 -0
- package/dist/esm/operations/cap/package.js +78 -0
- package/dist/esm/operations/cap/package.js.map +1 -0
- package/dist/esm/operations/cap/pull.d.ts +21 -0
- package/dist/esm/operations/cap/pull.js +78 -0
- package/dist/esm/operations/cap/pull.js.map +1 -0
- package/dist/esm/operations/cap/uninstall.d.ts +46 -0
- package/dist/esm/operations/cap/uninstall.js +98 -0
- package/dist/esm/operations/cap/uninstall.js.map +1 -0
- package/dist/esm/operations/cap/validate.d.ts +47 -0
- package/dist/esm/operations/cap/validate.js +235 -0
- package/dist/esm/operations/cap/validate.js.map +1 -0
- package/dist/esm/operations/jobs/site-archive.d.ts +1 -0
- package/dist/esm/operations/jobs/site-archive.js +30 -39
- package/dist/esm/operations/jobs/site-archive.js.map +1 -1
- package/dist/esm/operations/util/zip.d.ts +5 -0
- package/dist/esm/operations/util/zip.js +26 -0
- package/dist/esm/operations/util/zip.js.map +1 -0
- package/dist/esm/plugins/loader.js +6 -1
- package/dist/esm/plugins/loader.js.map +1 -1
- package/dist/esm/skills/github.d.ts +2 -18
- package/dist/esm/skills/github.js +195 -108
- package/dist/esm/skills/github.js.map +1 -1
- package/dist/esm/skills/index.d.ts +2 -1
- package/dist/esm/skills/index.js +2 -0
- package/dist/esm/skills/index.js.map +1 -1
- package/dist/esm/skills/parser.d.ts +2 -2
- package/dist/esm/skills/parser.js +13 -18
- package/dist/esm/skills/parser.js.map +1 -1
- package/dist/esm/skills/sources.d.ts +4 -0
- package/dist/esm/skills/sources.js +44 -0
- package/dist/esm/skills/sources.js.map +1 -0
- package/dist/esm/skills/types.d.ts +16 -1
- package/package.json +13 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Commerce App Package (CAP) operations for B2C Commerce.
|
|
8
|
+
*
|
|
9
|
+
* This module provides functions for validating, packaging, installing, and
|
|
10
|
+
* uninstalling Commerce App Packages (CAPs) on B2C Commerce instances.
|
|
11
|
+
*
|
|
12
|
+
* ## CAP Operations
|
|
13
|
+
*
|
|
14
|
+
* - {@link validateCap} - Validate CAP structure and manifest (local, no instance required)
|
|
15
|
+
* - {@link commerceAppInstall} - Install a CAP via the sfcc-install-commerce-app job
|
|
16
|
+
* - {@link commerceAppUninstall} - Uninstall a CAP via the sfcc-uninstall-commerce-app job
|
|
17
|
+
* - {@link commerceAppPackage} - Package a CAP directory into a distributable .zip
|
|
18
|
+
* - {@link discoverLocalApps} - Discover local CAPs by finding commerce-app.json files
|
|
19
|
+
* - {@link listInstalledApps} - List installed apps on an instance via commerce_feature_states export
|
|
20
|
+
*
|
|
21
|
+
* ## Usage
|
|
22
|
+
*
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import {
|
|
25
|
+
* validateCap,
|
|
26
|
+
* commerceAppInstall,
|
|
27
|
+
* commerceAppUninstall,
|
|
28
|
+
* commerceAppPackage,
|
|
29
|
+
* discoverLocalApps,
|
|
30
|
+
* listInstalledApps,
|
|
31
|
+
* } from '@salesforce/b2c-tooling-sdk/operations/cap';
|
|
32
|
+
*
|
|
33
|
+
* // Validate locally
|
|
34
|
+
* const result = await validateCap('./my-commerce-app');
|
|
35
|
+
* if (!result.valid) console.error(result.errors);
|
|
36
|
+
*
|
|
37
|
+
* // Package for distribution
|
|
38
|
+
* const pkg = await commerceAppPackage('./my-commerce-app');
|
|
39
|
+
*
|
|
40
|
+
* // Install on an instance
|
|
41
|
+
* await commerceAppInstall(instance, './my-commerce-app', { siteId: 'RefArch' });
|
|
42
|
+
*
|
|
43
|
+
* // Uninstall
|
|
44
|
+
* await commerceAppUninstall(instance, 'my-app', 'tax', { siteId: 'RefArch' });
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @module operations/cap
|
|
48
|
+
*/
|
|
49
|
+
export { validateCap } from './validate.js';
|
|
50
|
+
// Re-export JobExecutionError for convenience in CLI commands
|
|
51
|
+
export { JobExecutionError } from '../jobs/run.js';
|
|
52
|
+
export { commerceAppInstall, readManifest } from './install.js';
|
|
53
|
+
export { commerceAppUninstall } from './uninstall.js';
|
|
54
|
+
export { commerceAppPackage } from './package.js';
|
|
55
|
+
export { discoverLocalApps, listInstalledApps, parseCommerceFeatureStatesXml } from './list.js';
|
|
56
|
+
export { pullCommerceApps } from './pull.js';
|
|
57
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/operations/cap/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAG1C,8DAA8D;AAC9D,OAAO,EAAC,iBAAiB,EAAC,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EAAC,kBAAkB,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AAG9D,OAAO,EAAC,oBAAoB,EAAC,MAAM,gBAAgB,CAAC;AAGpD,OAAO,EAAC,kBAAkB,EAAC,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAC,iBAAiB,EAAE,iBAAiB,EAAE,6BAA6B,EAAC,MAAM,WAAW,CAAC;AAE9F,OAAO,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { B2CInstance } from '../../instance/index.js';
|
|
2
|
+
import { type JobExecution, type WaitForJobOptions } from '../jobs/run.js';
|
|
3
|
+
import { type CommerceAppManifest } from './validate.js';
|
|
4
|
+
/**
|
|
5
|
+
* Options for CAP installation.
|
|
6
|
+
*/
|
|
7
|
+
export interface CommerceAppInstallOptions {
|
|
8
|
+
/** Target site ID to install the app on. */
|
|
9
|
+
siteId: string;
|
|
10
|
+
/** Keep the uploaded zip on the instance after install (default: false). */
|
|
11
|
+
keepArchive?: boolean;
|
|
12
|
+
/** Wait options for job completion. */
|
|
13
|
+
waitOptions?: WaitForJobOptions;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Result of a CAP installation.
|
|
17
|
+
*/
|
|
18
|
+
export interface CommerceAppInstallResult {
|
|
19
|
+
/** Job execution details. */
|
|
20
|
+
execution: JobExecution;
|
|
21
|
+
/** App name (id from commerce-app.json). */
|
|
22
|
+
appName: string;
|
|
23
|
+
/** App version. */
|
|
24
|
+
appVersion: string;
|
|
25
|
+
/** Uploaded archive filename. */
|
|
26
|
+
archiveFilename: string;
|
|
27
|
+
/** Whether the archive was kept on the instance. */
|
|
28
|
+
archiveKept: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Installs a Commerce App Package (CAP) on a B2C Commerce instance.
|
|
32
|
+
*
|
|
33
|
+
* Accepts a local directory or zip file. Reads the commerce-app.json manifest
|
|
34
|
+
* to determine app name, version, and domain. Uploads the zip to WebDAV and
|
|
35
|
+
* executes the sfcc-install-commerce-app system job.
|
|
36
|
+
*
|
|
37
|
+
* @param instance - B2C instance to install to
|
|
38
|
+
* @param target - Path to a CAP directory or .zip file
|
|
39
|
+
* @param options - Install options including required siteId
|
|
40
|
+
* @returns Install result with job execution details
|
|
41
|
+
* @throws JobExecutionError if the install job fails
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const result = await commerceAppInstall(instance, './commerce-avalara-tax-app-v0.2.5', {
|
|
46
|
+
* siteId: 'RefArch',
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function commerceAppInstall(instance: B2CInstance, target: string, options: CommerceAppInstallOptions): Promise<CommerceAppInstallResult>;
|
|
51
|
+
export declare function readManifest(capDir: string): CommerceAppManifest;
|
|
52
|
+
/** Prefix site ID with "Sites-" if not already present. */
|
|
53
|
+
export declare function normalizeSiteId(siteId: string): string;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Commerce App Package (CAP) installation.
|
|
8
|
+
*
|
|
9
|
+
* Uploads a CAP to WebDAV and runs the sfcc-install-commerce-app system job.
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from 'node:fs';
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import JSZip from 'jszip';
|
|
14
|
+
import { getLogger } from '../../logging/logger.js';
|
|
15
|
+
import { waitForJob, JobExecutionError, getJobLog } from '../jobs/run.js';
|
|
16
|
+
import { addDirectoryToZip } from '../util/zip.js';
|
|
17
|
+
const INSTALL_JOB_ID = 'sfcc-install-commerce-app';
|
|
18
|
+
/**
|
|
19
|
+
* Installs a Commerce App Package (CAP) on a B2C Commerce instance.
|
|
20
|
+
*
|
|
21
|
+
* Accepts a local directory or zip file. Reads the commerce-app.json manifest
|
|
22
|
+
* to determine app name, version, and domain. Uploads the zip to WebDAV and
|
|
23
|
+
* executes the sfcc-install-commerce-app system job.
|
|
24
|
+
*
|
|
25
|
+
* @param instance - B2C instance to install to
|
|
26
|
+
* @param target - Path to a CAP directory or .zip file
|
|
27
|
+
* @param options - Install options including required siteId
|
|
28
|
+
* @returns Install result with job execution details
|
|
29
|
+
* @throws JobExecutionError if the install job fails
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const result = await commerceAppInstall(instance, './commerce-avalara-tax-app-v0.2.5', {
|
|
34
|
+
* siteId: 'RefArch',
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export async function commerceAppInstall(instance, target, options) {
|
|
39
|
+
const logger = getLogger();
|
|
40
|
+
const { siteId: rawSiteId, keepArchive = false, waitOptions } = options;
|
|
41
|
+
const siteId = normalizeSiteId(rawSiteId);
|
|
42
|
+
if (!fs.existsSync(target)) {
|
|
43
|
+
throw new Error(`Target not found: ${target}`);
|
|
44
|
+
}
|
|
45
|
+
const stat = fs.statSync(target);
|
|
46
|
+
let archiveContent;
|
|
47
|
+
let archiveFilename;
|
|
48
|
+
let manifest;
|
|
49
|
+
if (stat.isDirectory()) {
|
|
50
|
+
manifest = readManifest(target);
|
|
51
|
+
archiveFilename = `${manifest.id}-v${manifest.version}.zip`;
|
|
52
|
+
logger.debug({ path: target }, `Packaging CAP directory: ${target}`);
|
|
53
|
+
archiveContent = await createArchiveFromDirectory(target, `${manifest.id}-v${manifest.version}`);
|
|
54
|
+
}
|
|
55
|
+
else if (stat.isFile() && target.endsWith('.zip')) {
|
|
56
|
+
manifest = await readManifestFromZip(target);
|
|
57
|
+
archiveFilename = path.basename(target);
|
|
58
|
+
archiveContent = await fs.promises.readFile(target);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
throw new Error(`Target must be a directory or .zip file: ${target}`);
|
|
62
|
+
}
|
|
63
|
+
const uploadDir = 'Impex/commerce-apps';
|
|
64
|
+
const webdavUploadPath = `${uploadDir}/${archiveFilename}`;
|
|
65
|
+
const appPath = `webdav/Sites/${webdavUploadPath}`;
|
|
66
|
+
logger.debug({ path: webdavUploadPath }, `Uploading CAP to ${webdavUploadPath}`);
|
|
67
|
+
await instance.webdav.mkcol(uploadDir);
|
|
68
|
+
await instance.webdav.put(webdavUploadPath, archiveContent, 'application/zip');
|
|
69
|
+
logger.debug({ path: webdavUploadPath }, `CAP uploaded: ${webdavUploadPath}`);
|
|
70
|
+
// Execute the install job
|
|
71
|
+
logger.debug({ jobId: INSTALL_JOB_ID, appName: manifest.id, siteId }, `Executing ${INSTALL_JOB_ID} job`);
|
|
72
|
+
let execution;
|
|
73
|
+
// Try direct body format first (standard OCAPI format)
|
|
74
|
+
const { data, error } = await instance.ocapi.POST('/jobs/{job_id}/executions', {
|
|
75
|
+
params: { path: { job_id: INSTALL_JOB_ID } },
|
|
76
|
+
body: {
|
|
77
|
+
app_name: manifest.id,
|
|
78
|
+
app_source: 'WebDAV',
|
|
79
|
+
app_domain: manifest.domain,
|
|
80
|
+
site_id: siteId,
|
|
81
|
+
app_path: appPath,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
if (error?.fault?.type === 'UnknownPropertyException' &&
|
|
85
|
+
error.fault.arguments?.document === 'job_execution_request') {
|
|
86
|
+
// Retry with parameters format (internal/support users)
|
|
87
|
+
logger.warn('Retrying with parameters format for internal users');
|
|
88
|
+
const { data: retryData, error: retryError } = await instance.ocapi.POST('/jobs/{job_id}/executions', {
|
|
89
|
+
params: { path: { job_id: INSTALL_JOB_ID } },
|
|
90
|
+
body: {
|
|
91
|
+
parameters: [
|
|
92
|
+
{ name: 'AppName', value: manifest.id },
|
|
93
|
+
{ name: 'AppSource', value: 'WebDAV' },
|
|
94
|
+
{ name: 'AppDomain', value: manifest.domain },
|
|
95
|
+
{ name: 'SiteId', value: siteId },
|
|
96
|
+
{ name: 'AppPath', value: appPath },
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
if (retryError || !retryData) {
|
|
101
|
+
throw new Error(retryError?.fault?.message ?? 'Failed to start install job');
|
|
102
|
+
}
|
|
103
|
+
execution = retryData;
|
|
104
|
+
}
|
|
105
|
+
else if (error || !data) {
|
|
106
|
+
throw new Error(error?.fault?.message ?? 'Failed to start install job');
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
execution = data;
|
|
110
|
+
}
|
|
111
|
+
logger.debug({ jobId: INSTALL_JOB_ID, executionId: execution.id }, `Install job started: ${execution.id}`);
|
|
112
|
+
// Wait for job completion
|
|
113
|
+
let finalExecution;
|
|
114
|
+
try {
|
|
115
|
+
finalExecution = await waitForJob(instance, INSTALL_JOB_ID, execution.id, waitOptions);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
if (err instanceof JobExecutionError) {
|
|
119
|
+
try {
|
|
120
|
+
const log = await getJobLog(instance, err.execution);
|
|
121
|
+
logger.error({ jobId: INSTALL_JOB_ID, log }, `Job log:\n${log}`);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
logger.error({ jobId: INSTALL_JOB_ID }, 'Could not retrieve job log');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
// Clean up archive unless keeping
|
|
130
|
+
if (!keepArchive) {
|
|
131
|
+
await instance.webdav.delete(webdavUploadPath);
|
|
132
|
+
logger.debug({ path: webdavUploadPath }, `Archive deleted: ${webdavUploadPath}`);
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
execution: finalExecution,
|
|
136
|
+
appName: manifest.id,
|
|
137
|
+
appVersion: manifest.version,
|
|
138
|
+
archiveFilename,
|
|
139
|
+
archiveKept: keepArchive,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function readManifest(capDir) {
|
|
143
|
+
const manifestPath = path.join(capDir, 'commerce-app.json');
|
|
144
|
+
if (!fs.existsSync(manifestPath)) {
|
|
145
|
+
throw new Error(`commerce-app.json not found in: ${capDir}`);
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
return JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
throw new Error(`Failed to parse commerce-app.json in: ${capDir}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function readManifestFromZip(zipPath) {
|
|
155
|
+
const data = await fs.promises.readFile(zipPath);
|
|
156
|
+
const zip = await JSZip.loadAsync(data);
|
|
157
|
+
// Find commerce-app.json at root or one level deep
|
|
158
|
+
for (const [filePath, entry] of Object.entries(zip.files)) {
|
|
159
|
+
if (!entry.dir && (filePath === 'commerce-app.json' || filePath.match(/^[^/]+\/commerce-app\.json$/))) {
|
|
160
|
+
try {
|
|
161
|
+
const content = await entry.async('string');
|
|
162
|
+
return JSON.parse(content);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
throw new Error('Failed to parse commerce-app.json from zip');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
throw new Error('commerce-app.json not found in zip');
|
|
170
|
+
}
|
|
171
|
+
/** Prefix site ID with "Sites-" if not already present. */
|
|
172
|
+
export function normalizeSiteId(siteId) {
|
|
173
|
+
return siteId.startsWith('Sites-') ? siteId : `Sites-${siteId}`;
|
|
174
|
+
}
|
|
175
|
+
async function createArchiveFromDirectory(dirPath, archiveDirName) {
|
|
176
|
+
const zip = new JSZip();
|
|
177
|
+
const rootFolder = zip.folder(archiveDirName);
|
|
178
|
+
await addDirectoryToZip(rootFolder, dirPath);
|
|
179
|
+
return zip.generateAsync({ type: 'nodebuffer', compression: 'DEFLATE', compressionOptions: { level: 9 } });
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../../src/operations/cap/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { B2CInstance } from '../../instance/index.js';
|
|
2
|
+
import type { JobExecution, WaitForJobOptions } from '../jobs/run.js';
|
|
3
|
+
import type { CommerceAppManifest } from './validate.js';
|
|
4
|
+
/**
|
|
5
|
+
* A commerce feature state parsed from the commerce-feature-states.xml export.
|
|
6
|
+
*/
|
|
7
|
+
export interface CommerceFeatureState {
|
|
8
|
+
siteId: string;
|
|
9
|
+
featureName: string;
|
|
10
|
+
featureType: string;
|
|
11
|
+
featureSource: string;
|
|
12
|
+
featureDomain: string;
|
|
13
|
+
installStatus: string;
|
|
14
|
+
configStatus: string;
|
|
15
|
+
featureVersionId: string;
|
|
16
|
+
installedAt: string;
|
|
17
|
+
configTasks?: unknown[];
|
|
18
|
+
installationMetadata?: unknown;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A locally discovered Commerce App Package.
|
|
22
|
+
*/
|
|
23
|
+
export interface LocalCommerceApp {
|
|
24
|
+
/** Absolute path to the directory containing commerce-app.json. */
|
|
25
|
+
path: string;
|
|
26
|
+
/** Parsed manifest from commerce-app.json. */
|
|
27
|
+
manifest: CommerceAppManifest;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Options for listing installed apps on an instance.
|
|
31
|
+
*/
|
|
32
|
+
export interface ListInstalledAppsOptions {
|
|
33
|
+
/** Specific site IDs to query. If omitted, discovers all sites via OCAPI. */
|
|
34
|
+
sites?: string[];
|
|
35
|
+
/** Wait options for the export job. */
|
|
36
|
+
waitOptions?: WaitForJobOptions;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Result of listing installed apps on an instance.
|
|
40
|
+
*/
|
|
41
|
+
export interface ListInstalledAppsResult {
|
|
42
|
+
/** Parsed commerce feature states from all queried sites. */
|
|
43
|
+
features: CommerceFeatureState[];
|
|
44
|
+
/** Job execution details. */
|
|
45
|
+
execution: JobExecution;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Discovers local Commerce App Packages by searching for commerce-app.json files.
|
|
49
|
+
*
|
|
50
|
+
* Walks the directory tree starting from `searchPath`, finds directories
|
|
51
|
+
* containing a `commerce-app.json` file, and reads each manifest.
|
|
52
|
+
*
|
|
53
|
+
* @param searchPath - Root directory to search
|
|
54
|
+
* @returns Array of discovered local apps with their paths and manifests
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const apps = await discoverLocalApps('./my-workspace');
|
|
59
|
+
* for (const app of apps) {
|
|
60
|
+
* console.log(`${app.manifest.id}@${app.manifest.version} at ${app.path}`);
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function discoverLocalApps(searchPath: string): Promise<LocalCommerceApp[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Lists installed Commerce Apps on a B2C instance by exporting commerce feature states.
|
|
67
|
+
*
|
|
68
|
+
* Attempts to export the `commerce_feature_states` data unit for each site.
|
|
69
|
+
* If the export fails (e.g. because the data unit is not yet supported on the server),
|
|
70
|
+
* falls back to a bundled stub fixture.
|
|
71
|
+
*
|
|
72
|
+
* @param instance - B2C instance to query
|
|
73
|
+
* @param options - Options including optional site filter and wait options
|
|
74
|
+
* @returns List of commerce feature states across all queried sites
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* const result = await listInstalledApps(instance);
|
|
79
|
+
* for (const state of result.features) {
|
|
80
|
+
* console.log(`${state.featureName} (${state.installStatus}) on ${state.siteId}`);
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function listInstalledApps(instance: B2CInstance, options?: ListInstalledAppsOptions): Promise<ListInstalledAppsResult>;
|
|
85
|
+
/**
|
|
86
|
+
* Parses a commerce-feature-states.xml string into CommerceFeatureState objects.
|
|
87
|
+
*
|
|
88
|
+
* @param xml - XML string to parse
|
|
89
|
+
* @param siteId - Site ID to associate with parsed states (used as fallback if not in XML attributes)
|
|
90
|
+
* @returns Array of parsed commerce feature states
|
|
91
|
+
*/
|
|
92
|
+
export declare function parseCommerceFeatureStatesXml(xml: string, siteId: string): Promise<CommerceFeatureState[]>;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Commerce App listing operations.
|
|
8
|
+
*
|
|
9
|
+
* Provides functions for discovering local Commerce App Packages and listing
|
|
10
|
+
* installed apps on a B2C Commerce instance via site archive export.
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import JSZip from 'jszip';
|
|
15
|
+
import * as xml2js from 'xml2js';
|
|
16
|
+
import { getLogger } from '../../logging/logger.js';
|
|
17
|
+
import { siteArchiveExportToBuffer } from '../jobs/site-archive.js';
|
|
18
|
+
import { readManifest } from './install.js';
|
|
19
|
+
/**
|
|
20
|
+
* Discovers local Commerce App Packages by searching for commerce-app.json files.
|
|
21
|
+
*
|
|
22
|
+
* Walks the directory tree starting from `searchPath`, finds directories
|
|
23
|
+
* containing a `commerce-app.json` file, and reads each manifest.
|
|
24
|
+
*
|
|
25
|
+
* @param searchPath - Root directory to search
|
|
26
|
+
* @returns Array of discovered local apps with their paths and manifests
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const apps = await discoverLocalApps('./my-workspace');
|
|
31
|
+
* for (const app of apps) {
|
|
32
|
+
* console.log(`${app.manifest.id}@${app.manifest.version} at ${app.path}`);
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export async function discoverLocalApps(searchPath) {
|
|
37
|
+
const logger = getLogger();
|
|
38
|
+
const apps = [];
|
|
39
|
+
const resolvedPath = path.resolve(searchPath);
|
|
40
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
41
|
+
return apps;
|
|
42
|
+
}
|
|
43
|
+
logger.debug({ searchPath: resolvedPath }, `Discovering local CAPs in: ${resolvedPath}`);
|
|
44
|
+
findCommerceApps(resolvedPath, apps, logger);
|
|
45
|
+
logger.debug({ count: apps.length }, `Found ${apps.length} local CAP(s)`);
|
|
46
|
+
return apps;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Recursively finds directories containing commerce-app.json.
|
|
50
|
+
* Stops descending into a directory once a commerce-app.json is found there.
|
|
51
|
+
*/
|
|
52
|
+
function findCommerceApps(dir, apps, logger) {
|
|
53
|
+
let entries;
|
|
54
|
+
try {
|
|
55
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const manifestPath = path.join(dir, 'commerce-app.json');
|
|
61
|
+
if (fs.existsSync(manifestPath)) {
|
|
62
|
+
try {
|
|
63
|
+
const manifest = readManifest(dir);
|
|
64
|
+
apps.push({ path: dir, manifest });
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
logger.warn({ path: manifestPath, error: err }, `Skipping invalid commerce-app.json: ${manifestPath}`);
|
|
68
|
+
}
|
|
69
|
+
// Don't recurse into CAP directories
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
74
|
+
findCommerceApps(path.join(dir, entry.name), apps, logger);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Lists installed Commerce Apps on a B2C instance by exporting commerce feature states.
|
|
80
|
+
*
|
|
81
|
+
* Attempts to export the `commerce_feature_states` data unit for each site.
|
|
82
|
+
* If the export fails (e.g. because the data unit is not yet supported on the server),
|
|
83
|
+
* falls back to a bundled stub fixture.
|
|
84
|
+
*
|
|
85
|
+
* @param instance - B2C instance to query
|
|
86
|
+
* @param options - Options including optional site filter and wait options
|
|
87
|
+
* @returns List of commerce feature states across all queried sites
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const result = await listInstalledApps(instance);
|
|
92
|
+
* for (const state of result.features) {
|
|
93
|
+
* console.log(`${state.featureName} (${state.installStatus}) on ${state.siteId}`);
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export async function listInstalledApps(instance, options = {}) {
|
|
98
|
+
const logger = getLogger();
|
|
99
|
+
const { waitOptions } = options;
|
|
100
|
+
// Determine which sites to query
|
|
101
|
+
let siteIds;
|
|
102
|
+
if (options.sites && options.sites.length > 0) {
|
|
103
|
+
siteIds = options.sites;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
logger.debug('No sites specified, discovering all sites via OCAPI');
|
|
107
|
+
const { data, error } = await instance.ocapi.GET('/sites', {
|
|
108
|
+
params: { query: { select: '(**)' } },
|
|
109
|
+
});
|
|
110
|
+
if (error || !data) {
|
|
111
|
+
throw new Error(error?.fault?.message ?? 'Failed to list sites');
|
|
112
|
+
}
|
|
113
|
+
siteIds = (data.data ?? []).map((s) => s.id).filter((id) => !!id);
|
|
114
|
+
logger.debug({ siteIds }, `Discovered ${siteIds.length} site(s)`);
|
|
115
|
+
}
|
|
116
|
+
if (siteIds.length === 0) {
|
|
117
|
+
return { features: [], execution: undefined };
|
|
118
|
+
}
|
|
119
|
+
// Build export configuration for all sites with commerce_feature_states
|
|
120
|
+
const sitesConfig = {};
|
|
121
|
+
for (const siteId of siteIds) {
|
|
122
|
+
sitesConfig[siteId] = { commerce_feature_states: true };
|
|
123
|
+
}
|
|
124
|
+
logger.debug({ siteIds }, 'Exporting commerce_feature_states');
|
|
125
|
+
const exportResult = await siteArchiveExportToBuffer(instance, { sites: sitesConfig }, { waitOptions });
|
|
126
|
+
const states = await parseExportArchive(exportResult.data);
|
|
127
|
+
return { features: states, execution: exportResult.execution };
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Parses a site archive export zip for commerce-feature-states.xml files.
|
|
131
|
+
*/
|
|
132
|
+
async function parseExportArchive(data) {
|
|
133
|
+
const logger = getLogger();
|
|
134
|
+
const zip = await JSZip.loadAsync(data);
|
|
135
|
+
const states = [];
|
|
136
|
+
const filePaths = Object.keys(zip.files).filter((p) => !zip.files[p].dir);
|
|
137
|
+
logger.debug({ filePaths }, `Export archive contains ${filePaths.length} file(s)`);
|
|
138
|
+
for (const [filePath, entry] of Object.entries(zip.files)) {
|
|
139
|
+
if (entry.dir)
|
|
140
|
+
continue;
|
|
141
|
+
const match = filePath.match(/sites\/([^/]+)\/commerce-feature-states\.xml$/);
|
|
142
|
+
if (match) {
|
|
143
|
+
const siteId = match[1];
|
|
144
|
+
const xml = await entry.async('string');
|
|
145
|
+
const parsed = await parseCommerceFeatureStatesXml(xml, siteId);
|
|
146
|
+
states.push(...parsed);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return states;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Parses a commerce-feature-states.xml string into CommerceFeatureState objects.
|
|
153
|
+
*
|
|
154
|
+
* @param xml - XML string to parse
|
|
155
|
+
* @param siteId - Site ID to associate with parsed states (used as fallback if not in XML attributes)
|
|
156
|
+
* @returns Array of parsed commerce feature states
|
|
157
|
+
*/
|
|
158
|
+
export async function parseCommerceFeatureStatesXml(xml, siteId) {
|
|
159
|
+
const parsed = await xml2js.parseStringPromise(xml, {
|
|
160
|
+
explicitArray: false,
|
|
161
|
+
tagNameProcessors: [(name) => name.replace(/^[^:]+:/, '')],
|
|
162
|
+
});
|
|
163
|
+
if (!parsed)
|
|
164
|
+
return [];
|
|
165
|
+
const root = parsed['commerce-feature-states'];
|
|
166
|
+
if (!root)
|
|
167
|
+
return [];
|
|
168
|
+
let entries = root['commerce-feature-state'];
|
|
169
|
+
if (!entries)
|
|
170
|
+
return [];
|
|
171
|
+
// Normalize to array (xml2js uses single object when there's only one element)
|
|
172
|
+
if (!Array.isArray(entries)) {
|
|
173
|
+
entries = [entries];
|
|
174
|
+
}
|
|
175
|
+
return entries.map((entry) => {
|
|
176
|
+
const attrs = (entry['$'] ?? {});
|
|
177
|
+
// Parse config-tasks as JSON if present
|
|
178
|
+
let configTasks;
|
|
179
|
+
if (typeof entry['config-tasks'] === 'string') {
|
|
180
|
+
try {
|
|
181
|
+
configTasks = JSON.parse(entry['config-tasks']);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Leave as undefined if not valid JSON
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Parse installation-metadata as JSON if present
|
|
188
|
+
let installationMetadata;
|
|
189
|
+
if (typeof entry['installation-metadata'] === 'string') {
|
|
190
|
+
try {
|
|
191
|
+
const parsed = JSON.parse(entry['installation-metadata']);
|
|
192
|
+
if (typeof parsed.impexUninstallData === 'string') {
|
|
193
|
+
try {
|
|
194
|
+
parsed.impexUninstallData = JSON.parse(parsed.impexUninstallData);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// leave as string
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
installationMetadata = parsed;
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// Leave as undefined if not valid JSON
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
siteId: attrs['site-id'] || siteId,
|
|
208
|
+
featureName: attrs['feature-name'] || '',
|
|
209
|
+
featureType: getTextValue(entry['feature-type']),
|
|
210
|
+
featureSource: getTextValue(entry['feature-source']),
|
|
211
|
+
featureDomain: getTextValue(entry['feature-domain']),
|
|
212
|
+
installStatus: getTextValue(entry['install-status']),
|
|
213
|
+
configStatus: getTextValue(entry['config-status']),
|
|
214
|
+
featureVersionId: getTextValue(entry['feature-version-id']),
|
|
215
|
+
installedAt: getTextValue(entry['installed-at']),
|
|
216
|
+
configTasks,
|
|
217
|
+
installationMetadata,
|
|
218
|
+
};
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
/** Extract text value from xml2js parsed element (may be string or object with _). */
|
|
222
|
+
function getTextValue(value) {
|
|
223
|
+
if (typeof value === 'string')
|
|
224
|
+
return value;
|
|
225
|
+
if (value && typeof value === 'object' && '_' in value) {
|
|
226
|
+
return String(value['_']);
|
|
227
|
+
}
|
|
228
|
+
return '';
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../../src/operations/cap/list.ts"],"names":[],"mappings":"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"}
|