@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,231 @@
|
|
|
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 { B2CInstance } from '../../instance/index.js';
|
|
17
|
+
import { getLogger } from '../../logging/logger.js';
|
|
18
|
+
import { siteArchiveExportToBuffer } from '../jobs/site-archive.js';
|
|
19
|
+
import { readManifest } from './install.js';
|
|
20
|
+
/**
|
|
21
|
+
* Discovers local Commerce App Packages by searching for commerce-app.json files.
|
|
22
|
+
*
|
|
23
|
+
* Walks the directory tree starting from `searchPath`, finds directories
|
|
24
|
+
* containing a `commerce-app.json` file, and reads each manifest.
|
|
25
|
+
*
|
|
26
|
+
* @param searchPath - Root directory to search
|
|
27
|
+
* @returns Array of discovered local apps with their paths and manifests
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const apps = await discoverLocalApps('./my-workspace');
|
|
32
|
+
* for (const app of apps) {
|
|
33
|
+
* console.log(`${app.manifest.id}@${app.manifest.version} at ${app.path}`);
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export async function discoverLocalApps(searchPath) {
|
|
38
|
+
const logger = getLogger();
|
|
39
|
+
const apps = [];
|
|
40
|
+
const resolvedPath = path.resolve(searchPath);
|
|
41
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
42
|
+
return apps;
|
|
43
|
+
}
|
|
44
|
+
logger.debug({ searchPath: resolvedPath }, `Discovering local CAPs in: ${resolvedPath}`);
|
|
45
|
+
findCommerceApps(resolvedPath, apps, logger);
|
|
46
|
+
logger.debug({ count: apps.length }, `Found ${apps.length} local CAP(s)`);
|
|
47
|
+
return apps;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Recursively finds directories containing commerce-app.json.
|
|
51
|
+
* Stops descending into a directory once a commerce-app.json is found there.
|
|
52
|
+
*/
|
|
53
|
+
function findCommerceApps(dir, apps, logger) {
|
|
54
|
+
let entries;
|
|
55
|
+
try {
|
|
56
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const manifestPath = path.join(dir, 'commerce-app.json');
|
|
62
|
+
if (fs.existsSync(manifestPath)) {
|
|
63
|
+
try {
|
|
64
|
+
const manifest = readManifest(dir);
|
|
65
|
+
apps.push({ path: dir, manifest });
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
logger.warn({ path: manifestPath, error: err }, `Skipping invalid commerce-app.json: ${manifestPath}`);
|
|
69
|
+
}
|
|
70
|
+
// Don't recurse into CAP directories
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
75
|
+
findCommerceApps(path.join(dir, entry.name), apps, logger);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Lists installed Commerce Apps on a B2C instance by exporting commerce feature states.
|
|
81
|
+
*
|
|
82
|
+
* Attempts to export the `commerce_feature_states` data unit for each site.
|
|
83
|
+
* If the export fails (e.g. because the data unit is not yet supported on the server),
|
|
84
|
+
* falls back to a bundled stub fixture.
|
|
85
|
+
*
|
|
86
|
+
* @param instance - B2C instance to query
|
|
87
|
+
* @param options - Options including optional site filter and wait options
|
|
88
|
+
* @returns List of commerce feature states across all queried sites
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* const result = await listInstalledApps(instance);
|
|
93
|
+
* for (const state of result.features) {
|
|
94
|
+
* console.log(`${state.featureName} (${state.installStatus}) on ${state.siteId}`);
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export async function listInstalledApps(instance, options = {}) {
|
|
99
|
+
const logger = getLogger();
|
|
100
|
+
const { waitOptions } = options;
|
|
101
|
+
// Determine which sites to query
|
|
102
|
+
let siteIds;
|
|
103
|
+
if (options.sites && options.sites.length > 0) {
|
|
104
|
+
siteIds = options.sites;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
logger.debug('No sites specified, discovering all sites via OCAPI');
|
|
108
|
+
const { data, error } = await instance.ocapi.GET('/sites', {
|
|
109
|
+
params: { query: { select: '(**)' } },
|
|
110
|
+
});
|
|
111
|
+
if (error || !data) {
|
|
112
|
+
throw new Error(error?.fault?.message ?? 'Failed to list sites');
|
|
113
|
+
}
|
|
114
|
+
siteIds = (data.data ?? []).map((s) => s.id).filter((id) => !!id);
|
|
115
|
+
logger.debug({ siteIds }, `Discovered ${siteIds.length} site(s)`);
|
|
116
|
+
}
|
|
117
|
+
if (siteIds.length === 0) {
|
|
118
|
+
return { features: [], execution: undefined };
|
|
119
|
+
}
|
|
120
|
+
// Build export configuration for all sites with commerce_feature_states
|
|
121
|
+
const sitesConfig = {};
|
|
122
|
+
for (const siteId of siteIds) {
|
|
123
|
+
sitesConfig[siteId] = { commerce_feature_states: true };
|
|
124
|
+
}
|
|
125
|
+
logger.debug({ siteIds }, 'Exporting commerce_feature_states');
|
|
126
|
+
const exportResult = await siteArchiveExportToBuffer(instance, { sites: sitesConfig }, { waitOptions });
|
|
127
|
+
const states = await parseExportArchive(exportResult.data);
|
|
128
|
+
return { features: states, execution: exportResult.execution };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Parses a site archive export zip for commerce-feature-states.xml files.
|
|
132
|
+
*/
|
|
133
|
+
async function parseExportArchive(data) {
|
|
134
|
+
const logger = getLogger();
|
|
135
|
+
const zip = await JSZip.loadAsync(data);
|
|
136
|
+
const states = [];
|
|
137
|
+
const filePaths = Object.keys(zip.files).filter((p) => !zip.files[p].dir);
|
|
138
|
+
logger.debug({ filePaths }, `Export archive contains ${filePaths.length} file(s)`);
|
|
139
|
+
for (const [filePath, entry] of Object.entries(zip.files)) {
|
|
140
|
+
if (entry.dir)
|
|
141
|
+
continue;
|
|
142
|
+
const match = filePath.match(/sites\/([^/]+)\/commerce-feature-states\.xml$/);
|
|
143
|
+
if (match) {
|
|
144
|
+
const siteId = match[1];
|
|
145
|
+
const xml = await entry.async('string');
|
|
146
|
+
const parsed = await parseCommerceFeatureStatesXml(xml, siteId);
|
|
147
|
+
states.push(...parsed);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return states;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Parses a commerce-feature-states.xml string into CommerceFeatureState objects.
|
|
154
|
+
*
|
|
155
|
+
* @param xml - XML string to parse
|
|
156
|
+
* @param siteId - Site ID to associate with parsed states (used as fallback if not in XML attributes)
|
|
157
|
+
* @returns Array of parsed commerce feature states
|
|
158
|
+
*/
|
|
159
|
+
export async function parseCommerceFeatureStatesXml(xml, siteId) {
|
|
160
|
+
const parsed = await xml2js.parseStringPromise(xml, {
|
|
161
|
+
explicitArray: false,
|
|
162
|
+
tagNameProcessors: [(name) => name.replace(/^[^:]+:/, '')],
|
|
163
|
+
});
|
|
164
|
+
if (!parsed)
|
|
165
|
+
return [];
|
|
166
|
+
const root = parsed['commerce-feature-states'];
|
|
167
|
+
if (!root)
|
|
168
|
+
return [];
|
|
169
|
+
let entries = root['commerce-feature-state'];
|
|
170
|
+
if (!entries)
|
|
171
|
+
return [];
|
|
172
|
+
// Normalize to array (xml2js uses single object when there's only one element)
|
|
173
|
+
if (!Array.isArray(entries)) {
|
|
174
|
+
entries = [entries];
|
|
175
|
+
}
|
|
176
|
+
return entries.map((entry) => {
|
|
177
|
+
const attrs = (entry['$'] ?? {});
|
|
178
|
+
// Parse config-tasks as JSON if present
|
|
179
|
+
let configTasks;
|
|
180
|
+
if (typeof entry['config-tasks'] === 'string') {
|
|
181
|
+
try {
|
|
182
|
+
configTasks = JSON.parse(entry['config-tasks']);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// Leave as undefined if not valid JSON
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Parse installation-metadata as JSON if present
|
|
189
|
+
let installationMetadata;
|
|
190
|
+
if (typeof entry['installation-metadata'] === 'string') {
|
|
191
|
+
try {
|
|
192
|
+
const parsed = JSON.parse(entry['installation-metadata']);
|
|
193
|
+
if (typeof parsed.impexUninstallData === 'string') {
|
|
194
|
+
try {
|
|
195
|
+
parsed.impexUninstallData = JSON.parse(parsed.impexUninstallData);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// leave as string
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
installationMetadata = parsed;
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Leave as undefined if not valid JSON
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
siteId: attrs['site-id'] || siteId,
|
|
209
|
+
featureName: attrs['feature-name'] || '',
|
|
210
|
+
featureType: getTextValue(entry['feature-type']),
|
|
211
|
+
featureSource: getTextValue(entry['feature-source']),
|
|
212
|
+
featureDomain: getTextValue(entry['feature-domain']),
|
|
213
|
+
installStatus: getTextValue(entry['install-status']),
|
|
214
|
+
configStatus: getTextValue(entry['config-status']),
|
|
215
|
+
featureVersionId: getTextValue(entry['feature-version-id']),
|
|
216
|
+
installedAt: getTextValue(entry['installed-at']),
|
|
217
|
+
configTasks,
|
|
218
|
+
installationMetadata,
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/** Extract text value from xml2js parsed element (may be string or object with _). */
|
|
223
|
+
function getTextValue(value) {
|
|
224
|
+
if (typeof value === 'string')
|
|
225
|
+
return value;
|
|
226
|
+
if (value && typeof value === 'object' && '_' in value) {
|
|
227
|
+
return String(value['_']);
|
|
228
|
+
}
|
|
229
|
+
return '';
|
|
230
|
+
}
|
|
231
|
+
//# 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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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type CommerceAppManifest } from './validate.js';
|
|
2
|
+
/**
|
|
3
|
+
* Options for CAP packaging.
|
|
4
|
+
*/
|
|
5
|
+
export interface CommerceAppPackageOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Output path for the zip file.
|
|
8
|
+
* - If a directory: zip is written to `{outputPath}/{id}-v{version}.zip`
|
|
9
|
+
* - If a .zip path: written to that exact location
|
|
10
|
+
* - Default: current working directory
|
|
11
|
+
*/
|
|
12
|
+
outputPath?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Result of CAP packaging.
|
|
16
|
+
*/
|
|
17
|
+
export interface CommerceAppPackageResult {
|
|
18
|
+
/** Absolute path to the produced zip file. */
|
|
19
|
+
outputPath: string;
|
|
20
|
+
/** Parsed manifest. */
|
|
21
|
+
manifest: CommerceAppManifest;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Packages a CAP directory into a distributable .zip file.
|
|
25
|
+
*
|
|
26
|
+
* The zip root directory is named `{id}-v{version}/` as required by the CAP spec.
|
|
27
|
+
* Reads commerce-app.json to determine the app name and version.
|
|
28
|
+
*
|
|
29
|
+
* @param sourceDir - Path to the CAP directory
|
|
30
|
+
* @param options - Packaging options
|
|
31
|
+
* @returns Result with the output zip path and manifest
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const result = await commerceAppPackage('./commerce-avalara-tax-app-v0.2.5');
|
|
36
|
+
* console.log(`Packaged to: ${result.outputPath}`);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function commerceAppPackage(sourceDir: string, options?: CommerceAppPackageOptions): Promise<CommerceAppPackageResult>;
|
|
@@ -0,0 +1,78 @@
|
|
|
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) packaging.
|
|
8
|
+
*
|
|
9
|
+
* Zips a CAP directory into a distributable .zip file with the correct
|
|
10
|
+
* root directory naming convention ({id}-v{version}/).
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import JSZip from 'jszip';
|
|
15
|
+
import { getLogger } from '../../logging/logger.js';
|
|
16
|
+
import { addDirectoryToZip } from '../util/zip.js';
|
|
17
|
+
import { readManifest } from './install.js';
|
|
18
|
+
import {} from './validate.js';
|
|
19
|
+
/**
|
|
20
|
+
* Packages a CAP directory into a distributable .zip file.
|
|
21
|
+
*
|
|
22
|
+
* The zip root directory is named `{id}-v{version}/` as required by the CAP spec.
|
|
23
|
+
* Reads commerce-app.json to determine the app name and version.
|
|
24
|
+
*
|
|
25
|
+
* @param sourceDir - Path to the CAP directory
|
|
26
|
+
* @param options - Packaging options
|
|
27
|
+
* @returns Result with the output zip path and manifest
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const result = await commerceAppPackage('./commerce-avalara-tax-app-v0.2.5');
|
|
32
|
+
* console.log(`Packaged to: ${result.outputPath}`);
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export async function commerceAppPackage(sourceDir, options = {}) {
|
|
36
|
+
const logger = getLogger();
|
|
37
|
+
if (!fs.existsSync(sourceDir)) {
|
|
38
|
+
throw new Error(`Source directory not found: ${sourceDir}`);
|
|
39
|
+
}
|
|
40
|
+
if (!fs.statSync(sourceDir).isDirectory()) {
|
|
41
|
+
throw new Error(`Source must be a directory: ${sourceDir}`);
|
|
42
|
+
}
|
|
43
|
+
const manifest = readManifest(sourceDir);
|
|
44
|
+
if (!manifest.id || !manifest.version) {
|
|
45
|
+
throw new Error('commerce-app.json must have "id" and "version" fields');
|
|
46
|
+
}
|
|
47
|
+
const archiveDirName = `${manifest.id}-v${manifest.version}`;
|
|
48
|
+
const zipFilename = `${archiveDirName}.zip`;
|
|
49
|
+
// Determine output path
|
|
50
|
+
let outputZipPath;
|
|
51
|
+
if (!options.outputPath) {
|
|
52
|
+
outputZipPath = path.resolve(process.cwd(), zipFilename);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const resolved = path.resolve(options.outputPath);
|
|
56
|
+
if (resolved.endsWith('.zip')) {
|
|
57
|
+
outputZipPath = resolved;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
outputZipPath = path.join(resolved, zipFilename);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Ensure output directory exists
|
|
64
|
+
await fs.promises.mkdir(path.dirname(outputZipPath), { recursive: true });
|
|
65
|
+
logger.debug({ sourceDir, outputPath: outputZipPath }, `Packaging CAP: ${archiveDirName}`);
|
|
66
|
+
const zip = new JSZip();
|
|
67
|
+
const rootFolder = zip.folder(archiveDirName);
|
|
68
|
+
await addDirectoryToZip(rootFolder, sourceDir);
|
|
69
|
+
const buffer = await zip.generateAsync({
|
|
70
|
+
type: 'nodebuffer',
|
|
71
|
+
compression: 'DEFLATE',
|
|
72
|
+
compressionOptions: { level: 9 },
|
|
73
|
+
});
|
|
74
|
+
await fs.promises.writeFile(outputZipPath, buffer);
|
|
75
|
+
logger.debug({ outputPath: outputZipPath }, `CAP packaged to: ${outputZipPath}`);
|
|
76
|
+
return { outputPath: outputZipPath, manifest };
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=package.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.js","sourceRoot":"","sources":["../../../../src/operations/cap/package.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { B2CInstance } from '../../instance/index.js';
|
|
2
|
+
import type { CommerceFeatureState } from './list.js';
|
|
3
|
+
export interface PullCommerceAppsOptions {
|
|
4
|
+
outputDir?: string;
|
|
5
|
+
}
|
|
6
|
+
export type PullSource = 'instance' | 'github';
|
|
7
|
+
export interface PulledApp {
|
|
8
|
+
featureName: string;
|
|
9
|
+
version: string;
|
|
10
|
+
domain: string;
|
|
11
|
+
source: PullSource;
|
|
12
|
+
extractedPath: string;
|
|
13
|
+
}
|
|
14
|
+
export interface PullCommerceAppsResult {
|
|
15
|
+
pulled: PulledApp[];
|
|
16
|
+
failed: Array<{
|
|
17
|
+
featureName: string;
|
|
18
|
+
error: string;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
export declare function pullCommerceApps(instance: B2CInstance, features: CommerceFeatureState[], options?: PullCommerceAppsOptions): Promise<PullCommerceAppsResult>;
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
import * as fs from 'node:fs';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
import JSZip from 'jszip';
|
|
9
|
+
import { B2CInstance } from '../../instance/index.js';
|
|
10
|
+
import { getLogger } from '../../logging/logger.js';
|
|
11
|
+
const GITHUB_RAW_BASE = 'https://raw.githubusercontent.com/SalesforceCommerceCloud/commerce-apps/main';
|
|
12
|
+
export async function pullCommerceApps(instance, features, options = {}) {
|
|
13
|
+
const logger = getLogger();
|
|
14
|
+
const outputDir = path.resolve(options.outputDir ?? 'commerce-apps');
|
|
15
|
+
await fs.promises.mkdir(outputDir, { recursive: true });
|
|
16
|
+
const pulled = [];
|
|
17
|
+
const failed = [];
|
|
18
|
+
for (const feature of features) {
|
|
19
|
+
const { featureName, featureVersionId, featureDomain } = feature;
|
|
20
|
+
if (!featureVersionId) {
|
|
21
|
+
failed.push({ featureName, error: 'No version available' });
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const zipFilename = `${featureName}-v${featureVersionId}.zip`;
|
|
25
|
+
const webdavPath = `Impex/commerce-apps/${zipFilename}`;
|
|
26
|
+
let zipData = null;
|
|
27
|
+
let source = 'instance';
|
|
28
|
+
// Try instance first
|
|
29
|
+
if (await instance.webdav.exists(webdavPath)) {
|
|
30
|
+
logger.debug({ path: webdavPath }, `Downloading ${zipFilename} from instance`);
|
|
31
|
+
zipData = Buffer.from(await instance.webdav.get(webdavPath));
|
|
32
|
+
}
|
|
33
|
+
// Fall back to GitHub
|
|
34
|
+
if (!zipData) {
|
|
35
|
+
const githubUrl = `${GITHUB_RAW_BASE}/${featureDomain}/${featureName}/${zipFilename}`;
|
|
36
|
+
logger.warn({ url: githubUrl }, `${zipFilename} not found on instance, trying GitHub`);
|
|
37
|
+
source = 'github';
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(githubUrl);
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
failed.push({ featureName, error: `Not found on instance or GitHub (${githubUrl})` });
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
zipData = Buffer.from(await response.arrayBuffer());
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
48
|
+
failed.push({ featureName, error: `GitHub download failed: ${msg}` });
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Extract zip
|
|
53
|
+
const zip = await JSZip.loadAsync(zipData);
|
|
54
|
+
const extractDir = path.join(outputDir, featureName);
|
|
55
|
+
await fs.promises.mkdir(extractDir, { recursive: true });
|
|
56
|
+
for (const [filePath, entry] of Object.entries(zip.files)) {
|
|
57
|
+
if (entry.dir)
|
|
58
|
+
continue;
|
|
59
|
+
// Strip the top-level archive directory (e.g. "avalara-tax-v1.1.0/...")
|
|
60
|
+
const parts = filePath.split('/');
|
|
61
|
+
const relativePath = parts.length > 1 ? parts.slice(1).join('/') : filePath;
|
|
62
|
+
const fullPath = path.join(extractDir, relativePath);
|
|
63
|
+
await fs.promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
64
|
+
const content = await entry.async('nodebuffer');
|
|
65
|
+
await fs.promises.writeFile(fullPath, content);
|
|
66
|
+
}
|
|
67
|
+
logger.debug({ featureName, extractDir }, `Extracted ${featureName} to ${extractDir}`);
|
|
68
|
+
pulled.push({
|
|
69
|
+
featureName,
|
|
70
|
+
version: featureVersionId,
|
|
71
|
+
domain: featureDomain,
|
|
72
|
+
source,
|
|
73
|
+
extractedPath: extractDir,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return { pulled, failed };
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=pull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../../../src/operations/cap/pull.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commerce App Package (CAP) uninstallation.
|
|
3
|
+
*
|
|
4
|
+
* Runs the sfcc-uninstall-commerce-app system job to remove an installed CAP.
|
|
5
|
+
*/
|
|
6
|
+
import { B2CInstance } from '../../instance/index.js';
|
|
7
|
+
import { type JobExecution, type WaitForJobOptions } from '../jobs/run.js';
|
|
8
|
+
/**
|
|
9
|
+
* Options for CAP uninstallation.
|
|
10
|
+
*/
|
|
11
|
+
export interface CommerceAppUninstallOptions {
|
|
12
|
+
/** Target site ID to uninstall the app from. */
|
|
13
|
+
siteId: string;
|
|
14
|
+
/** Wait options for job completion. */
|
|
15
|
+
waitOptions?: WaitForJobOptions;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Result of a CAP uninstallation.
|
|
19
|
+
*/
|
|
20
|
+
export interface CommerceAppUninstallResult {
|
|
21
|
+
/** Job execution details. */
|
|
22
|
+
execution: JobExecution;
|
|
23
|
+
/** App name that was uninstalled. */
|
|
24
|
+
appName: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Uninstalls a Commerce App from a B2C Commerce instance.
|
|
28
|
+
*
|
|
29
|
+
* Executes the sfcc-uninstall-commerce-app system job which removes cartridges,
|
|
30
|
+
* IMPEX data, and configuration associated with the app from the target site.
|
|
31
|
+
*
|
|
32
|
+
* @param instance - B2C instance to uninstall from
|
|
33
|
+
* @param appName - App ID (from commerce-app.json "id" field, e.g. "avalara-tax")
|
|
34
|
+
* @param appDomain - App domain (e.g. "tax", "shipping")
|
|
35
|
+
* @param options - Uninstall options including required siteId
|
|
36
|
+
* @returns Uninstall result with job execution details
|
|
37
|
+
* @throws JobExecutionError if the uninstall job fails
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const result = await commerceAppUninstall(instance, 'avalara-tax', 'tax', {
|
|
42
|
+
* siteId: 'RefArch',
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function commerceAppUninstall(instance: B2CInstance, appName: string, appDomain: string, options: CommerceAppUninstallOptions): Promise<CommerceAppUninstallResult>;
|
|
@@ -0,0 +1,98 @@
|
|
|
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) uninstallation.
|
|
8
|
+
*
|
|
9
|
+
* Runs the sfcc-uninstall-commerce-app system job to remove an installed CAP.
|
|
10
|
+
*/
|
|
11
|
+
import { B2CInstance } from '../../instance/index.js';
|
|
12
|
+
import { getLogger } from '../../logging/logger.js';
|
|
13
|
+
import { waitForJob, JobExecutionError, getJobLog } from '../jobs/run.js';
|
|
14
|
+
import { normalizeSiteId } from './install.js';
|
|
15
|
+
const UNINSTALL_JOB_ID = 'sfcc-uninstall-commerce-app';
|
|
16
|
+
/**
|
|
17
|
+
* Uninstalls a Commerce App from a B2C Commerce instance.
|
|
18
|
+
*
|
|
19
|
+
* Executes the sfcc-uninstall-commerce-app system job which removes cartridges,
|
|
20
|
+
* IMPEX data, and configuration associated with the app from the target site.
|
|
21
|
+
*
|
|
22
|
+
* @param instance - B2C instance to uninstall from
|
|
23
|
+
* @param appName - App ID (from commerce-app.json "id" field, e.g. "avalara-tax")
|
|
24
|
+
* @param appDomain - App domain (e.g. "tax", "shipping")
|
|
25
|
+
* @param options - Uninstall options including required siteId
|
|
26
|
+
* @returns Uninstall result with job execution details
|
|
27
|
+
* @throws JobExecutionError if the uninstall job fails
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const result = await commerceAppUninstall(instance, 'avalara-tax', 'tax', {
|
|
32
|
+
* siteId: 'RefArch',
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export async function commerceAppUninstall(instance, appName, appDomain, options) {
|
|
37
|
+
const logger = getLogger();
|
|
38
|
+
const { siteId: rawSiteId, waitOptions } = options;
|
|
39
|
+
const siteId = normalizeSiteId(rawSiteId);
|
|
40
|
+
logger.debug({ jobId: UNINSTALL_JOB_ID, appName, siteId }, `Executing ${UNINSTALL_JOB_ID} job`);
|
|
41
|
+
let execution;
|
|
42
|
+
// Try direct body format first (standard OCAPI format)
|
|
43
|
+
const { data, error } = await instance.ocapi.POST('/jobs/{job_id}/executions', {
|
|
44
|
+
params: { path: { job_id: UNINSTALL_JOB_ID } },
|
|
45
|
+
body: {
|
|
46
|
+
app_name: appName,
|
|
47
|
+
app_domain: appDomain,
|
|
48
|
+
site_id: siteId,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
if (error?.fault?.type === 'UnknownPropertyException' &&
|
|
52
|
+
error.fault.arguments?.document === 'job_execution_request') {
|
|
53
|
+
// Retry with parameters format (internal/support users)
|
|
54
|
+
logger.warn('Retrying with parameters format for internal users');
|
|
55
|
+
const { data: retryData, error: retryError } = await instance.ocapi.POST('/jobs/{job_id}/executions', {
|
|
56
|
+
params: { path: { job_id: UNINSTALL_JOB_ID } },
|
|
57
|
+
body: {
|
|
58
|
+
parameters: [
|
|
59
|
+
{ name: 'AppName', value: appName },
|
|
60
|
+
{ name: 'AppDomain', value: appDomain },
|
|
61
|
+
{ name: 'SiteId', value: siteId },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
if (retryError || !retryData) {
|
|
66
|
+
throw new Error(retryError?.fault?.message ?? 'Failed to start uninstall job');
|
|
67
|
+
}
|
|
68
|
+
execution = retryData;
|
|
69
|
+
}
|
|
70
|
+
else if (error || !data) {
|
|
71
|
+
throw new Error(error?.fault?.message ?? 'Failed to start uninstall job');
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
execution = data;
|
|
75
|
+
}
|
|
76
|
+
logger.debug({ jobId: UNINSTALL_JOB_ID, executionId: execution.id }, `Uninstall job started: ${execution.id}`);
|
|
77
|
+
let finalExecution;
|
|
78
|
+
try {
|
|
79
|
+
finalExecution = await waitForJob(instance, UNINSTALL_JOB_ID, execution.id, waitOptions);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
if (err instanceof JobExecutionError) {
|
|
83
|
+
try {
|
|
84
|
+
const log = await getJobLog(instance, err.execution);
|
|
85
|
+
logger.error({ jobId: UNINSTALL_JOB_ID, log }, `Job log:\n${log}`);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
logger.error({ jobId: UNINSTALL_JOB_ID }, 'Could not retrieve job log');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
throw err;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
execution: finalExecution,
|
|
95
|
+
appName,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=uninstall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../../../src/operations/cap/uninstall.ts"],"names":[],"mappings":"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"}
|