sf-decomposer 6.13.0 → 6.14.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/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@
5
5
 
6
6
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
7
7
 
8
+ ## [6.14.0](https://github.com/mcarvin8/sf-decomposer/compare/v6.13.0...v6.14.0) (2026-04-30)
9
+
10
+
11
+ ### Features
12
+
13
+ * add multiLevel override and round-trip verify command ([#418](https://github.com/mcarvin8/sf-decomposer/issues/418)) ([5f06265](https://github.com/mcarvin8/sf-decomposer/commit/5f06265333e9a0523154d0c745e83e0571938f98))
14
+ * **decompose:** expose splitTags as an override field ([#416](https://github.com/mcarvin8/sf-decomposer/issues/416)) ([eedf0f2](https://github.com/mcarvin8/sf-decomposer/commit/eedf0f23d248fdd028a097435aa9493449269954))
15
+
8
16
  ## [6.13.0](https://github.com/mcarvin8/sf-decomposer/compare/v6.12.0...v6.13.0) (2026-04-30)
9
17
 
10
18
 
package/README.md CHANGED
@@ -17,7 +17,8 @@ A Salesforce CLI plugin that **decomposes** large metadata XML files into smalle
17
17
  - [Commands](#commands)
18
18
  - [sf decomposer decompose](#sf-decomposer-decompose)
19
19
  - [sf decomposer recompose](#sf-decomposer-recompose)
20
- - [Manifest-scoped runs](#manifest-scoped-runs)
20
+ - [sf decomposer verify](#sf-decomposer-verify)
21
+ - [Manifest-scoped runs](#manifest-scoped-runs)
21
22
  - [Decompose Strategies](#decompose-strategies)
22
23
  - [Custom Labels](#custom-labels-decomposition)
23
24
  - [Permission Sets (grouped-by-tag)](#additional-permission-set-decomposition)
@@ -27,6 +28,8 @@ A Salesforce CLI plugin that **decomposes** large metadata XML files into smalle
27
28
  - [Troubleshooting](#troubleshooting)
28
29
  - [Hooks](#hooks)
29
30
  - [Per-Type & Per-Component Overrides](#per-type--per-component-overrides)
31
+ - [splitTags grammar](#splittags-grammar)
32
+ - [multiLevel grammar](#multilevel-grammar)
30
33
  - [Ignore Files](#ignore-files)
31
34
  - [.forceignore](#forceignore)
32
35
  - [.sfdecomposerignore](#sfdecomposerignore)
@@ -113,10 +116,11 @@ Salesforce’s built-in decomposition is limited. sf-decomposer gives admins and
113
116
 
114
117
  ## Commands
115
118
 
116
- | Command | Description |
117
- | ------------------------- | --------------------------------------------------------------- |
118
- | `sf decomposer decompose` | Decompose metadata in package directories into smaller files. |
119
- | `sf decomposer recompose` | Recompose decomposed files back into deployment-ready metadata. |
119
+ | Command | Description |
120
+ | ------------------------- | ----------------------------------------------------------------------------------- |
121
+ | `sf decomposer decompose` | Decompose metadata in package directories into smaller files. |
122
+ | `sf decomposer recompose` | Recompose decomposed files back into deployment-ready metadata. |
123
+ | `sf decomposer verify` | Round-trip check: decompose + recompose in a temp directory and diff the originals. |
120
124
 
121
125
  ### sf decomposer decompose
122
126
 
@@ -193,9 +197,49 @@ sf decomposer recompose -x "manifest/package.xml"
193
197
  sf project deploy start -x "manifest/package.xml"
194
198
  ```
195
199
 
196
- ### Manifest-scoped runs
200
+ ### sf decomposer verify
197
201
 
198
- The `-x` / `--manifest` flag accepts any standard Salesforce `package.xml` and limits the work to just the components it lists. This is especially useful for CI/CD pipelines that deploy a subset of metadata per change.
202
+ Non-destructive round-trip check: copies your package directories into a temp directory under your OS's `tmpdir()`, runs decompose then recompose there, and diffs the rebuilt parents against the originals using **structural XML equality** (sibling and attribute order are ignored). Exits non-zero on any drift; your working tree is never modified.
203
+
204
+ ```
205
+ USAGE
206
+ $ sf decomposer verify [-m <value>] [-x <value>] [-f <value>] [-i <value>] [-s <value>] [-p -c --json]
207
+
208
+ FLAGS
209
+ -m, --metadata-type=<value> Metadata suffix to verify (e.g. flow, labels). Repeatable. Optional when --manifest is provided.
210
+ -x, --manifest=<value> Path to a package.xml manifest. When provided, only the components listed in the manifest are verified.
211
+ -f, --format=<value> Output format used for the round-trip decompose: xml | yaml | json | json5 [default: xml]
212
+ -i, --ignore-package-directory=<value> Package directory to skip. Repeatable.
213
+ -s, --strategy=<value> unique-id | grouped-by-tag [default: unique-id]
214
+ -p, --decompose-nested-permissions With grouped-by-tag, further decompose permission set and muting permission set object/field permissions.
215
+ -c, --config Load per-type and per-component overrides from .sfdecomposer.config.json in the repo root, the same as `decompose --config`. [default: false]
216
+
217
+ GLOBAL FLAGS
218
+ --json Output as JSON.
219
+ ```
220
+
221
+ > At least one of `--metadata-type` or `--manifest` is required. When both are supplied, the run is scoped to the intersection of the two.
222
+
223
+ **Examples**
224
+
225
+ ```bash
226
+ # Verify two metadata types round-trip cleanly with defaults
227
+ sf decomposer verify -m "permissionset" -m "profile"
228
+
229
+ # Verify a different strategy + nested-perms split before committing the change
230
+ sf decomposer verify -m "permissionset" -s "grouped-by-tag" -p
231
+
232
+ # CI gate: verify just the components in a deploy manifest, using the repo-root config
233
+ sf decomposer verify -x "manifest/package.xml" --config
234
+ ```
235
+
236
+ Files where the **only** delta is sibling or attribute ordering are surfaced separately as informational notices ("Note: N file(s) round-tripped semantically but with sibling/attribute reordering") rather than as drift. This is safe — Salesforce treats metadata as order-agnostic and `config-disassembler` does not preserve original sibling order — but it tells you up front that committing the post-recompose output will produce a diff in git even though the metadata is functionally identical.
237
+
238
+ ---
239
+
240
+ ## Manifest-scoped runs
241
+
242
+ The `-x` / `--manifest` flag is supported by every `sf decomposer` command (`decompose`, `recompose`, `verify`) and accepts any standard Salesforce `package.xml`, limiting the work to just the components it lists. This is especially useful for CI/CD pipelines that deploy a subset of metadata per change.
199
243
 
200
244
  How it works:
201
245
 
@@ -413,7 +457,9 @@ By default, a single decompose run uses one format and one strategy across every
413
457
  | `components` | Optional (required if `metadataTypes` is omitted). Array of `<metadataSuffix>:<fullName>` keys (e.g. `permissionset:HR_Admin`, `report:MyFolder/MyReport`). Each component may appear in at most one override. |
414
458
  | `decomposedFormat` | `xml` \| `json` \| `json5` \| `yaml`. |
415
459
  | `strategy` | `unique-id` \| `grouped-by-tag`. Hard rules still win — `labels` and `loyaltyProgramSetup` are always treated as `unique-id`. |
416
- | `decomposeNestedPermissions` | Only applies to `permissionset` / `mutingpermissionset` with `grouped-by-tag`. |
460
+ | `decomposeNestedPermissions` | Only applies to `permissionset` / `mutingpermissionset` with `grouped-by-tag`. Sets a known-good `splitTags` default; ignored if `splitTags` is also set in the same scope. |
461
+ | `splitTags` | Custom `splitTags` spec for `grouped-by-tag` strategy. See [splitTags grammar](#splittags-grammar). Ignored when the resolved strategy is not `grouped-by-tag`. |
462
+ | `multiLevel` | Custom `multiLevel` spec for nested-array decomposition. See [multiLevel grammar](#multilevel-grammar). When set, replaces the hardcoded `loyaltyProgramSetup` default for the targeted scope. |
417
463
  | `prePurge` | Per-scope prePurge (decompose). Component-scope `prePurge` only purges the named component's decomposed directory. |
418
464
  | `postPurge` | Per-scope postPurge (decompose: remove originals after decomposing). |
419
465
 
@@ -439,6 +485,80 @@ For each component, each option is resolved independently in this order (highest
439
485
  3. The run-wide value (CLI flag, hook config top-level field, or built-in default).
440
486
  4. Hard plugin rules (e.g. `labels` and `loyaltyProgramSetup` forced to `unique-id`) override all of the above.
441
487
 
488
+ ### splitTags grammar
489
+
490
+ `splitTags` lets you control how `grouped-by-tag` writes nested arrays for any metadata type. The plugin already applies a known-good default for permission sets when `decomposeNestedPermissions: true` is set; setting `splitTags` directly takes precedence and works for any metadata type.
491
+
492
+ **Spec:** Comma-separated rules. Each rule has 3 or 4 colon-separated parts:
493
+
494
+ - `<tag>:<mode>:<field>` — read array items from the top-level `<tag>`.
495
+ - `<tag>:<path>:<mode>:<field>` — read array items from the nested `<path>` (defaults to `<tag>`).
496
+
497
+ `<mode>` is one of:
498
+
499
+ - **`split`** — write one file per array item, named after the value of `<field>` on each item.
500
+ - **`group`** — group array items by the value of `<field>`, writing one file per group.
501
+
502
+ Each `<tag>` may appear at most once in a spec. The plugin validates the grammar at config-load time. Deeper checks (e.g. unknown tag names for the metadata type) are surfaced by the underlying disassembler crate at runtime.
503
+
504
+ #### splitTags cookbook
505
+
506
+ ```json
507
+ "overrides": [
508
+ {
509
+ "metadataTypes": ["permissionset", "mutingpermissionset"],
510
+ "strategy": "grouped-by-tag",
511
+ "splitTags": "objectPermissions:split:object,fieldPermissions:group:field"
512
+ },
513
+ {
514
+ "metadataTypes": ["profile"],
515
+ "strategy": "grouped-by-tag",
516
+ "splitTags": "objectPermissions:split:object,fieldPermissions:group:field,layoutAssignments:group:layout"
517
+ },
518
+ {
519
+ "metadataTypes": ["flow"],
520
+ "strategy": "grouped-by-tag",
521
+ "splitTags": "actionCalls:split:name,decisions:split:name,assignments:split:name"
522
+ },
523
+ {
524
+ "metadataTypes": ["workflow"],
525
+ "strategy": "grouped-by-tag",
526
+ "splitTags": "rules:split:fullName,alerts:split:fullName,fieldUpdates:split:fullName,tasks:split:fullName"
527
+ }
528
+ ]
529
+ ```
530
+
531
+ > **Caveat:** When using `mode: split`, the chosen `<field>` must produce a unique value for every array item — otherwise two items would map to the same filename. If two items share a field value, prefer `mode: group` instead, which is designed for that case.
532
+
533
+ ### multiLevel grammar
534
+
535
+ `multiLevel` enables a second decomposition pass on inner-level files for metadata types whose XML has deeply nested repeatable blocks (e.g. `loyaltyProgramSetup`'s `programProcesses → parameters → ...`). The plugin already applies a known-good default for `loyaltyProgramSetup` when running the `unique-id` strategy; setting `multiLevel` directly takes precedence and works for any metadata type.
536
+
537
+ **Spec:** A single rule with exactly 3 colon-separated parts (the third part is itself a comma-separated list):
538
+
539
+ ```
540
+ <file_pattern>:<root_to_strip>:<unique_id_elements>
541
+ ```
542
+
543
+ - **`<file_pattern>`** — basename pattern that selects which inner-level files get the second decomposition pass (e.g. `programProcesses`).
544
+ - **`<root_to_strip>`** — XML root tag to strip from each matched file before splitting.
545
+ - **`<unique_id_elements>`** — comma-separated list of element names used to derive a stable filename for each inner-level item (e.g. `parameterName,ruleName`). The first element that resolves to a non-empty value wins.
546
+
547
+ The plugin validates the grammar at config-load time; deeper checks (whether the file pattern matches anything, whether the unique-id elements actually appear on the inner XML) are surfaced by the underlying disassembler crate at runtime.
548
+
549
+ ```json
550
+ "overrides": [
551
+ {
552
+ "metadataTypes": ["loyaltyProgramSetup"],
553
+ "multiLevel": "programProcesses:programProcesses:parameterName,ruleName"
554
+ }
555
+ ]
556
+ ```
557
+
558
+ > **Limit:** Only one rule per scope. The disassembler does not currently accept multiple comma-separated multiLevel specs, because the third part of the rule is itself a comma-separated list.
559
+
560
+ > **Tip:** Use [`sf decomposer verify`](#sf-decomposer-verify) to non-destructively confirm a new override config still round-trips before committing it.
561
+
442
562
  ### Opting in from the CLI
443
563
 
444
564
  CLI users can opt into overrides on `decompose` with the boolean `--config` (`-c`) flag. When set, the plugin reads `.sfdecomposer.config.json` from the repo root (the nearest ancestor directory that contains `sfdx-project.json`):
@@ -0,0 +1,17 @@
1
+ import { SfCommand } from '@salesforce/sf-plugins-core';
2
+ import { VerifyResult } from '../../helpers/types.js';
3
+ export default class DecomposerVerify extends SfCommand<VerifyResult> {
4
+ static readonly summary: string;
5
+ static readonly description: string;
6
+ static readonly examples: string[];
7
+ static readonly flags: {
8
+ 'metadata-type': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ manifest: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'ignore-package-directory': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ strategy: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'decompose-nested-permissions': import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ config: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ };
16
+ run(): Promise<VerifyResult>;
17
+ }
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+ import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
3
+ import { Messages } from '@salesforce/core';
4
+ import { DECOMPOSED_FILE_TYPES, DECOMPOSED_STRATEGIES } from '../../helpers/constants.js';
5
+ import { verifyMetadataTypes } from '../../core/verifyMetadataTypes.js';
6
+ import { loadOverridesFromConfig, resolveDefaultConfigPath } from '../../helpers/configOverrides.js';
7
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
8
+ const messages = Messages.loadMessages('sf-decomposer', 'decomposer.verify');
9
+ export default class DecomposerVerify extends SfCommand {
10
+ static summary = messages.getMessage('summary');
11
+ static description = messages.getMessage('description');
12
+ static examples = messages.getMessages('examples');
13
+ static flags = {
14
+ 'metadata-type': Flags.string({
15
+ summary: messages.getMessage('flags.metadata-type.summary'),
16
+ char: 'm',
17
+ multiple: true,
18
+ required: false,
19
+ }),
20
+ manifest: Flags.file({
21
+ summary: messages.getMessage('flags.manifest.summary'),
22
+ char: 'x',
23
+ required: false,
24
+ exists: true,
25
+ }),
26
+ format: Flags.string({
27
+ summary: messages.getMessage('flags.format.summary'),
28
+ char: 'f',
29
+ required: true,
30
+ multiple: false,
31
+ default: 'xml',
32
+ options: DECOMPOSED_FILE_TYPES,
33
+ }),
34
+ 'ignore-package-directory': Flags.directory({
35
+ summary: messages.getMessage('flags.ignore-package-directory.summary'),
36
+ char: 'i',
37
+ required: false,
38
+ multiple: true,
39
+ }),
40
+ strategy: Flags.string({
41
+ summary: messages.getMessage('flags.strategy.summary'),
42
+ char: 's',
43
+ required: true,
44
+ multiple: false,
45
+ default: 'unique-id',
46
+ options: DECOMPOSED_STRATEGIES,
47
+ }),
48
+ 'decompose-nested-permissions': Flags.boolean({
49
+ summary: messages.getMessage('flags.decompose-nested-permissions.summary'),
50
+ char: 'p',
51
+ required: false,
52
+ default: false,
53
+ }),
54
+ config: Flags.boolean({
55
+ summary: messages.getMessage('flags.config.summary'),
56
+ char: 'c',
57
+ required: false,
58
+ default: false,
59
+ }),
60
+ };
61
+ async run() {
62
+ const { flags } = await this.parse(DecomposerVerify);
63
+ if (!flags['metadata-type'] && !flags['manifest']) {
64
+ throw messages.createError('error.missingMetadataOrManifest');
65
+ }
66
+ const overrides = flags['config'] ? await loadOverridesFromConfig(await resolveDefaultConfigPath()) : undefined;
67
+ const result = await verifyMetadataTypes({
68
+ metadataTypes: flags['metadata-type'],
69
+ format: flags['format'],
70
+ ignoreDirs: flags['ignore-package-directory'],
71
+ strategy: flags['strategy'],
72
+ decomposeNestedPerms: flags['decompose-nested-permissions'],
73
+ manifest: flags['manifest'],
74
+ overrides,
75
+ log: this.log.bind(this),
76
+ });
77
+ if (result.drift.length > 0) {
78
+ throw messages.createError('error.driftDetected', [String(result.drift.length)]);
79
+ }
80
+ return result;
81
+ }
82
+ }
83
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/commands/decomposer/verify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { VerifyOptions, VerifyResult } from '../helpers/types.js';
2
+ /**
3
+ * Run a non-destructive round-trip check: copy the user's package directories into a scratch
4
+ * directory under the OS temp folder, decompose + recompose them there, and diff the rebuilt
5
+ * parents against the originals.
6
+ *
7
+ * The temp directory is always removed before this function returns, and the user's working tree
8
+ * is never modified. The returned `drift` array is empty when every parent XML survived the round
9
+ * trip byte-identically; otherwise each entry names the offending file (relative to its package
10
+ * directory) and a short reason.
11
+ */
12
+ export declare function verifyMetadataTypes(options: VerifyOptions): Promise<VerifyResult>;
@@ -0,0 +1,143 @@
1
+ 'use strict';
2
+ import { mkdtemp, rm, readFile, writeFile, cp, stat, mkdir } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { dirname, join, relative, resolve } from 'node:path';
5
+ import { getRepoRoot } from '../service/core/getRepoRoot.js';
6
+ import { diffDirectories } from '../service/verify/diffDirectories.js';
7
+ import { SFDX_PROJECT_FILE_NAME } from '../helpers/constants.js';
8
+ import { decomposeMetadataTypes } from './decomposeMetadataTypes.js';
9
+ import { recomposeMetadataTypes } from './recomposeMetadataTypes.js';
10
+ /**
11
+ * Run a non-destructive round-trip check: copy the user's package directories into a scratch
12
+ * directory under the OS temp folder, decompose + recompose them there, and diff the rebuilt
13
+ * parents against the originals.
14
+ *
15
+ * The temp directory is always removed before this function returns, and the user's working tree
16
+ * is never modified. The returned `drift` array is empty when every parent XML survived the round
17
+ * trip byte-identically; otherwise each entry names the offending file (relative to its package
18
+ * directory) and a short reason.
19
+ */
20
+ export async function verifyMetadataTypes(options) {
21
+ const { metadataTypes, format, ignoreDirs, strategy, decomposeNestedPerms, manifest, overrides, log } = options;
22
+ const { repoRoot, dxConfigFilePath } = (await getRepoRoot());
23
+ const sfdxProjectRaw = await readFile(dxConfigFilePath, 'utf-8');
24
+ const sfdxProject = JSON.parse(sfdxProjectRaw);
25
+ const packageDirRelPaths = sfdxProject.packageDirectories.map((p) => p.path);
26
+ const tempProjectDir = await mkdtemp(join(tmpdir(), 'sf-decomposer-verify-'));
27
+ const originalCwd = process.cwd();
28
+ try {
29
+ await Promise.all(packageDirRelPaths.map(async (rel) => {
30
+ const src = resolve(repoRoot, rel);
31
+ const dst = resolve(tempProjectDir, rel);
32
+ /* istanbul ignore next -- @preserve: declared package dirs typically exist; defensive only */
33
+ if (!(await pathExists(src))) {
34
+ /* istanbul ignore next -- @preserve: declared package dirs typically exist; defensive only */
35
+ return;
36
+ }
37
+ await cp(src, dst, { recursive: true });
38
+ }));
39
+ await writeFile(join(tempProjectDir, SFDX_PROJECT_FILE_NAME), sfdxProjectRaw);
40
+ // Manifests are validated by oclif's `Flags.file({ exists: true })`, so when one is supplied
41
+ // it always points to a real file under the user's repo. Mirror it into the temp project at
42
+ // the same relative path so parseManifest (which is repoRoot-relative once we chdir) finds it.
43
+ let tempManifest;
44
+ if (manifest) {
45
+ const absManifest = resolve(originalCwd, manifest);
46
+ const relManifest = relative(repoRoot, absManifest);
47
+ const tempManifestAbs = resolve(tempProjectDir, relManifest);
48
+ await mkdir(dirname(tempManifestAbs), { recursive: true });
49
+ await cp(absManifest, tempManifestAbs);
50
+ tempManifest = tempManifestAbs;
51
+ }
52
+ process.chdir(tempProjectDir);
53
+ // Strip any user-supplied prePurge/postPurge from the overrides for verify only. Verify needs
54
+ // the parent XML to survive the decompose phase so that manifest-driven recompose can
55
+ // re-resolve it (parseManifest only returns parent XML paths that exist on disk). Letting the
56
+ // user's overrides drive post-purge here would silently break manifest filtering.
57
+ const verifyOverrides = overrides?.map((override) => {
58
+ const { prePurge, postPurge, ...rest } = override;
59
+ // Reference the stripped fields so the linter understands they are intentionally discarded.
60
+ void prePurge;
61
+ void postPurge;
62
+ return rest;
63
+ });
64
+ const decomposed = await decomposeMetadataTypes({
65
+ metadataTypes,
66
+ // Wipe any pre-existing decomposed children so we always start from a clean fixture, but
67
+ // keep the parent XML intact for the recompose phase (see comment above).
68
+ prepurge: true,
69
+ postpurge: false,
70
+ format,
71
+ ignoreDirs,
72
+ strategy,
73
+ decomposeNestedPerms,
74
+ manifest: tempManifest,
75
+ overrides: verifyOverrides,
76
+ log,
77
+ });
78
+ if (decomposed.metadata.length > 0) {
79
+ await recomposeMetadataTypes({
80
+ metadataTypes: decomposed.metadata,
81
+ // Postpurge here removes the decomposed children we generated above, leaving the rebuilt
82
+ // parent XML as the only artifact to diff against the original.
83
+ postpurge: true,
84
+ ignoreDirs,
85
+ manifest: tempManifest,
86
+ log,
87
+ });
88
+ }
89
+ process.chdir(originalCwd);
90
+ const drift = [];
91
+ const reordered = [];
92
+ const diffTasks = packageDirRelPaths.map(async (rel) => {
93
+ const original = resolve(repoRoot, rel);
94
+ const reconstructed = resolve(tempProjectDir, rel);
95
+ /* istanbul ignore if -- @preserve: we just `cp`'d into this directory, so it always exists */
96
+ if (!(await pathExists(reconstructed))) {
97
+ return { drift: [], reordered: [] };
98
+ }
99
+ return diffDirectories(original, reconstructed);
100
+ });
101
+ for (const result of await Promise.all(diffTasks)) {
102
+ drift.push(...result.drift);
103
+ reordered.push(...result.reordered);
104
+ }
105
+ if (drift.length === 0) {
106
+ log(`Round-trip verified for ${decomposed.metadata.length} metadata type(s); no drift detected.`);
107
+ }
108
+ else {
109
+ log(`Round-trip drift detected in ${drift.length} file(s):`);
110
+ for (const entry of drift) {
111
+ log(` - ${entry.path}: ${entry.reason}`);
112
+ }
113
+ }
114
+ if (reordered.length > 0) {
115
+ // Informational only — semantic content matches, just sibling/attribute order changed.
116
+ // Salesforce treats metadata as order-agnostic, so this is safe to commit.
117
+ log(`Note: ${reordered.length} file(s) round-tripped semantically but with sibling/attribute reordering:`);
118
+ for (const path of reordered) {
119
+ log(` - ${path}`);
120
+ }
121
+ }
122
+ return { metadata: decomposed.metadata, drift, reordered };
123
+ }
124
+ finally {
125
+ /* istanbul ignore next -- @preserve: defensive guard; we already chdir back on the happy path */
126
+ if (process.cwd() !== originalCwd) {
127
+ process.chdir(originalCwd);
128
+ }
129
+ await rm(tempProjectDir, { recursive: true, force: true });
130
+ }
131
+ }
132
+ async function pathExists(path) {
133
+ try {
134
+ await stat(path);
135
+ return true;
136
+ }
137
+ catch {
138
+ /* istanbul ignore next -- @preserve: package directories declared in sfdx-project.json always
139
+ exist on disk in the supported flow; this catch is defensive for partially-broken projects. */
140
+ return false;
141
+ }
142
+ }
143
+ //# sourceMappingURL=verifyMetadataTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifyMetadataTypes.js","sourceRoot":"","sources":["../../src/core/verifyMetadataTypes.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,10 @@ export type ResolvedDecomposeTypeOptions = {
11
11
  decomposeNestedPerms: boolean;
12
12
  prepurge: boolean;
13
13
  postpurge: boolean;
14
+ /** Resolved custom `splitTags` spec, when explicitly set in an override. */
15
+ splitTags?: string;
16
+ /** Resolved custom `multiLevel` spec, when explicitly set in an override. */
17
+ multiLevel?: string;
14
18
  };
15
19
  /**
16
20
  * Load and validate the `overrides` array from a `.sfdecomposer.config.json` file.
@@ -22,6 +26,21 @@ export declare function loadOverridesFromConfig(configPath: string): Promise<Dec
22
26
  * Unknown override keys are tolerated (ignored), but forbidden run-scope keys throw.
23
27
  */
24
28
  export declare function validateOverrides(overrides: DecomposerOverride[]): void;
29
+ /**
30
+ * Validate the comma-separated `splitTags` spec at config-load time. Each rule must be of the
31
+ * form `<tag>:<mode>:<field>` or `<tag>:<path>:<mode>:<field>`, with `mode` ∈ {split, group}.
32
+ * Tags must be unique within the spec. Deeper validation (e.g. unknown XML tag names) is left
33
+ * to the underlying disassembler crate at runtime.
34
+ */
35
+ export declare function validateSplitTagsSpec(spec: string, i: number): void;
36
+ /**
37
+ * Validate the `multiLevel` spec at config-load time. The underlying disassembler currently
38
+ * accepts a single colon-separated rule of the form
39
+ * `<file_pattern>:<root_to_strip>:<unique_id_elements>` where `<unique_id_elements>` is itself
40
+ * a comma-separated list. Deeper checks (whether the file pattern matches anything, whether
41
+ * the unique-id elements actually exist on the inner XML) are left to the runtime crate.
42
+ */
43
+ export declare function validateMultiLevelSpec(spec: string, i: number): void;
25
44
  /**
26
45
  * Parse a component override key of the form `<metadataSuffix>:<fullName>`. Returns `undefined`
27
46
  * when the key is malformed. Only the first colon is treated as the delimiter so fullNames that
@@ -28,6 +28,7 @@ export async function resolveDefaultConfigPath() {
28
28
  // as either a recognized override field (validated below) or as a forward-compatible unknown
29
29
  // key (silently ignored).
30
30
  const FORBIDDEN_OVERRIDE_KEYS = new Set(['manifest', 'metadataSuffixes', 'ignorePackageDirectories']);
31
+ const SPLIT_TAGS_MODES = new Set(['split', 'group']);
31
32
  /**
32
33
  * Load and validate the `overrides` array from a `.sfdecomposer.config.json` file.
33
34
  * Returns an empty array if the file is missing, unreadable, or contains no overrides.
@@ -109,6 +110,89 @@ function validateOverrideValues(override, i) {
109
110
  throw new Error(`Override at index ${i} has invalid "strategy": "${override.strategy}". ` +
110
111
  `Allowed values: ${DECOMPOSED_STRATEGIES.join(', ')}.`);
111
112
  }
113
+ if (override.splitTags !== undefined) {
114
+ validateSplitTagsSpec(override.splitTags, i);
115
+ }
116
+ if (override.multiLevel !== undefined) {
117
+ validateMultiLevelSpec(override.multiLevel, i);
118
+ }
119
+ }
120
+ /**
121
+ * Validate the comma-separated `splitTags` spec at config-load time. Each rule must be of the
122
+ * form `<tag>:<mode>:<field>` or `<tag>:<path>:<mode>:<field>`, with `mode` ∈ {split, group}.
123
+ * Tags must be unique within the spec. Deeper validation (e.g. unknown XML tag names) is left
124
+ * to the underlying disassembler crate at runtime.
125
+ */
126
+ export function validateSplitTagsSpec(spec, i) {
127
+ if (typeof spec !== 'string' || spec.trim() === '') {
128
+ throw new Error(`Override at index ${i} has an empty "splitTags" string.`);
129
+ }
130
+ const rules = spec.split(',').map((rule) => rule.trim());
131
+ const seenTags = new Set();
132
+ for (const rule of rules) {
133
+ if (rule === '') {
134
+ throw new Error(`Override at index ${i} "splitTags" contains an empty rule.`);
135
+ }
136
+ const parts = rule.split(':').map((part) => part.trim());
137
+ let tag;
138
+ let mode;
139
+ let field;
140
+ if (parts.length === 3) {
141
+ [tag, mode, field] = parts;
142
+ }
143
+ else if (parts.length === 4) {
144
+ // path defaults to tag in the 3-part form; we don't need to retain it for validation,
145
+ // we just check the parts are non-empty and the mode/field are well-formed.
146
+ [tag, , mode, field] = parts;
147
+ }
148
+ else {
149
+ throw new Error(`Override at index ${i} "splitTags" rule "${rule}" must have 3 or 4 colon-separated parts ` +
150
+ '("tag:mode:field" or "tag:path:mode:field").');
151
+ }
152
+ if (!tag || !mode || !field || (parts.length === 4 && !parts[1])) {
153
+ throw new Error(`Override at index ${i} "splitTags" rule "${rule}" has empty parts.`);
154
+ }
155
+ if (!SPLIT_TAGS_MODES.has(mode)) {
156
+ throw new Error(`Override at index ${i} "splitTags" rule "${rule}" has invalid mode "${mode}". ` +
157
+ `Allowed values: ${Array.from(SPLIT_TAGS_MODES).join(', ')}.`);
158
+ }
159
+ if (seenTags.has(tag)) {
160
+ throw new Error(`Override at index ${i} "splitTags" contains duplicate tag "${tag}". Each tag may appear at most once.`);
161
+ }
162
+ seenTags.add(tag);
163
+ }
164
+ }
165
+ /**
166
+ * Validate the `multiLevel` spec at config-load time. The underlying disassembler currently
167
+ * accepts a single colon-separated rule of the form
168
+ * `<file_pattern>:<root_to_strip>:<unique_id_elements>` where `<unique_id_elements>` is itself
169
+ * a comma-separated list. Deeper checks (whether the file pattern matches anything, whether
170
+ * the unique-id elements actually exist on the inner XML) are left to the runtime crate.
171
+ */
172
+ export function validateMultiLevelSpec(spec, i) {
173
+ if (typeof spec !== 'string' || spec.trim() === '') {
174
+ throw new Error(`Override at index ${i} has an empty "multiLevel" string.`);
175
+ }
176
+ const parts = spec.split(':').map((part) => part.trim());
177
+ if (parts.length !== 3) {
178
+ throw new Error(`Override at index ${i} "multiLevel" must have exactly 3 colon-separated parts ` +
179
+ '("<file_pattern>:<root_to_strip>:<unique_id_elements>").');
180
+ }
181
+ const [filePattern, rootToStrip, uniqueIdElements] = parts;
182
+ if (!filePattern || !rootToStrip || !uniqueIdElements) {
183
+ throw new Error(`Override at index ${i} "multiLevel" has empty parts.`);
184
+ }
185
+ const ids = uniqueIdElements.split(',').map((id) => id.trim());
186
+ const seenIds = new Set();
187
+ for (const id of ids) {
188
+ if (id === '') {
189
+ throw new Error(`Override at index ${i} "multiLevel" unique-id list contains an empty entry.`);
190
+ }
191
+ if (seenIds.has(id)) {
192
+ throw new Error(`Override at index ${i} "multiLevel" unique-id list has duplicate entry "${id}".`);
193
+ }
194
+ seenIds.add(id);
195
+ }
112
196
  }
113
197
  function validateMetadataTypeEntries(metadataTypes, i, seenTypes) {
114
198
  for (const metadataType of metadataTypes) {
@@ -193,6 +277,8 @@ export function resolveDecomposeOptionsForType(metadataType, base, overrides) {
193
277
  decomposeNestedPerms: override.decomposeNestedPermissions ?? base.decomposeNestedPerms,
194
278
  prepurge: override.prePurge ?? base.prepurge,
195
279
  postpurge: override.postPurge ?? base.postpurge,
280
+ splitTags: override.splitTags ?? base.splitTags,
281
+ multiLevel: override.multiLevel ?? base.multiLevel,
196
282
  };
197
283
  }
198
284
  /**
@@ -214,6 +300,8 @@ export function resolveDecomposeOptionsForComponent(metadataType, fullName, type
214
300
  decomposeNestedPerms: componentOverride.decomposeNestedPermissions ?? typeResolved.decomposeNestedPerms,
215
301
  prepurge: componentOverride.prePurge ?? typeResolved.prepurge,
216
302
  postpurge: componentOverride.postPurge ?? typeResolved.postpurge,
303
+ splitTags: componentOverride.splitTags ?? typeResolved.splitTags,
304
+ multiLevel: componentOverride.multiLevel ?? typeResolved.multiLevel,
217
305
  };
218
306
  }
219
307
  //# sourceMappingURL=configOverrides.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"configOverrides.js","sourceRoot":"","sources":["../../src/helpers/configOverrides.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"configOverrides.js","sourceRoot":"","sources":["../../src/helpers/configOverrides.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,21 @@ export type DecomposerOverride = {
11
11
  decomposedFormat?: string;
12
12
  strategy?: string;
13
13
  decomposeNestedPermissions?: boolean;
14
+ /**
15
+ * Custom `splitTags` spec for `grouped-by-tag` strategy. Comma-separated rules of the form
16
+ * `<tag>:<mode>:<field>` or `<tag>:<path>:<mode>:<field>`, where `mode` is `split` (one file
17
+ * per array item, filename from `field`) or `group` (array items grouped by `field`, one file
18
+ * per group). When set, this wins over the hardcoded `decomposeNestedPermissions` default
19
+ * for permission sets. Only applied when the resolved strategy is `grouped-by-tag`.
20
+ */
21
+ splitTags?: string;
22
+ /**
23
+ * Custom `multiLevel` spec for nested-array decomposition. Format:
24
+ * `<file_pattern>:<root_to_strip>:<unique_id_elements>` (the third part is itself a
25
+ * comma-separated list). When set, this wins over the hardcoded `loyaltyProgramSetup`
26
+ * default. Applies regardless of strategy because multiLevel works on a per-file pattern.
27
+ */
28
+ multiLevel?: string;
14
29
  prePurge?: boolean;
15
30
  postPurge?: boolean;
16
31
  };
@@ -68,3 +83,30 @@ export type RecomposeOptions = {
68
83
  manifest?: string;
69
84
  log: (msg: string) => void;
70
85
  };
86
+ export type VerifyOptions = {
87
+ metadataTypes?: string[];
88
+ format: string;
89
+ ignoreDirs?: string[];
90
+ strategy: string;
91
+ decomposeNestedPerms: boolean;
92
+ manifest?: string;
93
+ overrides?: DecomposerOverride[];
94
+ log: (msg: string) => void;
95
+ };
96
+ export type VerifyDrift = {
97
+ /** Path of the offending file relative to its package directory. */
98
+ path: string;
99
+ /** Short human-readable reason: `'content drift'` or `'missing in round-trip output'`. */
100
+ reason: string;
101
+ };
102
+ export type VerifyResult = {
103
+ /** Metadata types that participated in the round trip. */
104
+ metadata: string[];
105
+ /** One entry per file that did not survive the round trip semantically. Empty on success. */
106
+ drift: VerifyDrift[];
107
+ /**
108
+ * Files where the only delta was sibling/attribute ordering — content is semantically identical
109
+ * but not byte-identical. Reported for awareness; does not fail `verify`.
110
+ */
111
+ reordered: string[];
112
+ };
@@ -88,18 +88,31 @@ async function decomposeFromManifest(manifestXmlPaths, uniqueIdElements, typeRes
88
88
  }
89
89
  function disassembleHandler(filePath, uniqueIdElements, options, ignorePath, metaSuffix) {
90
90
  const handler = new DisassembleXMLFileHandler();
91
- let multiLevel;
92
- let splitTags;
93
91
  const effectiveStrategy = applyHardStrategyRules(metaSuffix, options.strategy);
94
- const decomposePermSets = options.decomposeNestedPerms &&
95
- (metaSuffix === 'permissionset' || metaSuffix === 'mutingpermissionset') &&
96
- effectiveStrategy === 'grouped-by-tag';
97
- const decomposeLoyalyProgram = metaSuffix === 'loyaltyProgramSetup' && effectiveStrategy === 'unique-id';
98
- if (decomposeLoyalyProgram) {
92
+ // Resolve multiLevel with this precedence:
93
+ // 1. an explicit `multiLevel` set in the override (any metadata type);
94
+ // 2. the hardcoded loyaltyProgramSetup default when running unique-id strategy.
95
+ let multiLevel;
96
+ if (options.multiLevel) {
97
+ multiLevel = options.multiLevel;
98
+ }
99
+ else if (metaSuffix === 'loyaltyProgramSetup' && effectiveStrategy === 'unique-id') {
99
100
  multiLevel = 'programProcesses:programProcesses:parameterName,ruleName';
100
101
  }
101
- if (decomposePermSets) {
102
- splitTags = 'objectPermissions:split:object,fieldPermissions:group:field';
102
+ // Resolve splitTags with this precedence:
103
+ // 1. an explicit `splitTags` set in the override (any metadata type, gated to grouped-by-tag);
104
+ // 2. the hardcoded permission-set default when `decomposeNestedPermissions: true` is set on
105
+ // a permissionset / mutingpermissionset under grouped-by-tag.
106
+ // splitTags is a no-op for non-grouped-by-tag strategies, so we never pass it otherwise.
107
+ let splitTags;
108
+ if (effectiveStrategy === 'grouped-by-tag') {
109
+ if (options.splitTags) {
110
+ splitTags = options.splitTags;
111
+ }
112
+ else if (options.decomposeNestedPerms &&
113
+ (metaSuffix === 'permissionset' || metaSuffix === 'mutingpermissionset')) {
114
+ splitTags = 'objectPermissions:split:object,fieldPermissions:group:field';
115
+ }
103
116
  }
104
117
  handler.disassemble({
105
118
  filePath,
@@ -1 +1 @@
1
- {"version":3,"file":"decomposeFileHandler.js","sourceRoot":"","sources":["../../../src/service/decompose/decomposeFileHandler.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,MAAM,MAAM,SAAS,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEpF,OAAO,EAEL,4BAA4B,EAC5B,mCAAmC,GACpC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,cAMC,EACD,YAA0C,EAC1C,UAAkB,EAClB,SAAgC,EAChC,gBAA8B;IAE9B,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,UAAU,EAAE,gBAAgB,EAAE,GAAG,cAAc,CAAC;IAExG,IAAI,gBAAgB,IAAI,gBAAgB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QAClD,MAAM,qBAAqB,CACzB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,UAAU,EACV,SAAS,CACV,CAAC;QACF,OAAO;IACT,CAAC;IAED,gFAAgF;IAChF,MAAM,KAAK,GAAG,MAAM,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAEtD,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAC/C,KAAK,CAAC,KAAK,IAAI,EAAE;QACf,IAAI,mBAAmB,IAAI,UAAU,EAAE,CAAC;YACtC,MAAM,mBAAmB,CAAC,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;QAC7G,CAAC;aAAM,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;YACnC,qFAAqF;YACrF,4EAA4E;YAC5E,IAAI,YAAY,CAAC,QAAQ;gBAAE,MAAM,cAAc,CAAC,YAAY,CAAC,CAAC;YAC9D,MAAM,qBAAqB,GAAG,OAAO,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC;YACxE,MAAM,qBAAqB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,qBAAqB,CAAC,CAAC;YAE7E,kBAAkB,CAChB,qBAAqB,EACrB,gBAAgB,EAChB,EAAE,GAAG,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,EACpC,UAAU,EACV,UAAU,CACX,CAAC;YACF,MAAM,mBAAmB,CAAC,YAAY,CAAC,CAAC;QAC1C,CAAC;aAAM,IAAI,4BAA4B,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC;YAC/D,MAAM,cAAc,CAAC,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;QACxG,CAAC;aAAM,CAAC;YACN,kBAAkB,CAAC,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QAC3F,CAAC;QACD,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;YAC9B,MAAM,eAAe,CAAC,YAAY,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,gBAA6B,EAC7B,gBAAwB,EACxB,YAA0C,EAC1C,UAAkB,EAClB,UAAkB,EAClB,mBAA4B,EAC5B,UAAkB,EAClB,SAAgC;IAEhC,MAAM,KAAK,GAAG,MAAM,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAE9C,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5B,mFAAmF;QACnF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC/D,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CACnD,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,YAAY,CAAC,QAAQ;gBAAE,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;YAC1D,MAAM,qBAAqB,GAAG,OAAO,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;YACpE,MAAM,qBAAqB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,qBAAqB,CAAC,CAAC;YAC7E,kBAAkB,CAChB,qBAAqB,EACrB,gBAAgB,EAChB,EAAE,GAAG,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,EACpC,UAAU,EACV,UAAU,CACX,CAAC;YACF,MAAM,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC,CAAC,CACH,CAAC;QACF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzB,OAAO;IACT,CAAC;IAED,IAAI,mBAAmB,IAAI,UAAU,EAAE,CAAC;QACtC,iGAAiG;QACjG,gHAAgH;QAChH,qGAAqG;QACrG,mEAAmE;QACnE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CACrD,KAAK,CAAC,GAAG,EAAE;YACT,MAAM,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;YACrC,MAAM,QAAQ,GAAG,mCAAmC,CAAC,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;YACpG,OAAO,kBAAkB,CAAC,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QAC3F,CAAC,CAAC,CACH,CAAC;QACF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzB,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACrC,KAAK,CAAC,GAAG,EAAE;QACT,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAG,mCAAmC,CAAC,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;QACpG,OAAO,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;IACzF,CAAC,CAAC,CACH,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEzB,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;QAC9B,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClE,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;YACvC,4CAA4C;YAC5C,MAAM,eAAe,CAAC,WAAW,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CACzB,QAAgB,EAChB,gBAAwB,EACxB,OAAqC,EACrC,UAAkB,EAClB,UAAkB;IAElB,MAAM,OAAO,GAA8B,IAAI,yBAAyB,EAAE,CAAC;IAC3E,IAAI,UAAU,CAAC;IACf,IAAI,SAAS,CAAC;IACd,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/E,MAAM,iBAAiB,GACrB,OAAO,CAAC,oBAAoB;QAC5B,CAAC,UAAU,KAAK,eAAe,IAAI,UAAU,KAAK,qBAAqB,CAAC;QACxE,iBAAiB,KAAK,gBAAgB,CAAC;IACzC,MAAM,sBAAsB,GAAY,UAAU,KAAK,qBAAqB,IAAI,iBAAiB,KAAK,WAAW,CAAC;IAClH,IAAI,sBAAsB,EAAE,CAAC;QAC3B,UAAU,GAAG,0DAA0D,CAAC;IAC1E,CAAC;IAED,IAAI,iBAAiB,EAAE,CAAC;QACtB,SAAS,GAAG,6DAA6D,CAAC;IAC5E,CAAC;IAED,OAAO,CAAC,WAAW,CAAC;QAClB,QAAQ;QACR,gBAAgB;QAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,UAAU;QACV,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,QAAQ,EAAE,iBAAiB;QAC3B,UAAU;QACV,SAAS;KACV,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAS,sBAAsB,CAAC,UAAkB,EAAE,QAAgB;IAClE,IAAI,QAAQ,KAAK,gBAAgB;QAAE,OAAO,QAAQ,CAAC;IACnD,IAAI,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,qBAAqB;QAAE,OAAO,WAAW,CAAC;IACxF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,eAAe,CAAC,QAAgB,EAAE,UAAkB;IAC3D,MAAM,UAAU,GAAG,IAAI,UAAU,WAAW,CAAC;IAC7C;oFACgF;IAChF,OAAO,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC1F,CAAC;AAED,KAAK,UAAU,mBAAmB,CAChC,YAAoB,EACpB,gBAAwB,EACxB,YAA0C,EAC1C,UAAkB,EAClB,UAAkB,EAClB,SAAgC;IAEhC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IAE7C,gDAAgD;IAChD,MAAM,SAAS,GAAG,MAAM,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAAC;IAC7D,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAC5C,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAChD,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACtD,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IAChC,CAAC,CAAC,CACH,CAAC;IACF,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAEpD,2CAA2C;IAC3C,MAAM,YAAY,GAAG,MAAM,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAC;IAC/D,MAAM,YAAY,GAAG,WAAW;SAC7B,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC;SAC5B,GAAG,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CACvB,YAAY,CAAC,GAAG,EAAE;QAChB,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACvC,MAAM,QAAQ,GAAG,mCAAmC,CAAC,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;QACpG,OAAO,kBAAkB,CAAC,WAAW,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;IAC7F,CAAC,CAAC,CACH,CAAC;IAEJ,MAAM,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,cAAc,CAC3B,YAAoB,EACpB,gBAAwB,EACxB,YAA0C,EAC1C,UAAkB,EAClB,UAAkB,EAClB,SAAgC;IAEhC,MAAM,UAAU,GAAG,IAAI,UAAU,WAAW,CAAC;IAC7C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACrE,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IAEhG,MAAM,KAAK,GAAG,MAAM,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACrC,KAAK,CAAC,GAAG,EAAE;QACT,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAG,mCAAmC,CAAC,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;QACpG,OAAO,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;IAC1F,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"decomposeFileHandler.js","sourceRoot":"","sources":["../../../src/service/decompose/decomposeFileHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { VerifyDrift } from '../../helpers/types.js';
2
+ export type DirDiffResult = {
3
+ /** Files whose contents are semantically different (real drift). */
4
+ drift: VerifyDrift[];
5
+ /**
6
+ * Files where the only delta is sibling/attribute ordering. Surfaced for awareness — these are
7
+ * NOT failures, since Salesforce metadata is generally order-agnostic and `config-disassembler`
8
+ * does not preserve original sibling order.
9
+ */
10
+ reordered: string[];
11
+ };
12
+ /**
13
+ * Recursively diff two directory trees. Files in the reference tree are compared against the
14
+ * mock tree; files that exist only in the mock tree are intentionally ignored, so the helper is
15
+ * safe to use against round-trip output that contains transient sidecars (e.g.
16
+ * `.config-disassembler.json`) which the original tree never had.
17
+ *
18
+ * For `.xml` files, comparison is **structural and order-insensitive**: sibling elements with the
19
+ * same tag name can appear in any order without registering as drift. Files that are byte-different
20
+ * but semantically equal are returned in `reordered` so the caller can surface the difference.
21
+ */
22
+ export declare function diffDirectories(referenceDir: string, mockDir: string, prefix?: string): Promise<DirDiffResult>;
23
+ /**
24
+ * Compare two XML strings for structural equality, ignoring sibling order and attribute order.
25
+ * Falls back to `false` if either side fails to parse, so genuinely malformed output still
26
+ * surfaces as drift through the caller.
27
+ */
28
+ export declare function xmlEquivalent(a: string, b: string): boolean;
29
+ /**
30
+ * Convert any JSON value into a stable string representation: object keys are sorted, and arrays
31
+ * are sorted by the canonical-JSON of each element. Two values produce the same canonical string
32
+ * iff they are deeply equal up to sibling order.
33
+ */
34
+ export declare function canonicalJson(value: unknown): string;
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+ import { readdir, readFile, stat } from 'node:fs/promises';
3
+ import { extname, join } from 'node:path';
4
+ import { XMLParser } from 'fast-xml-parser';
5
+ const xmlParser = new XMLParser({
6
+ ignoreAttributes: false,
7
+ attributeNamePrefix: '@_',
8
+ parseTagValue: false,
9
+ parseAttributeValue: false,
10
+ trimValues: true,
11
+ ignoreDeclaration: true,
12
+ ignorePiTags: true,
13
+ });
14
+ /**
15
+ * Recursively diff two directory trees. Files in the reference tree are compared against the
16
+ * mock tree; files that exist only in the mock tree are intentionally ignored, so the helper is
17
+ * safe to use against round-trip output that contains transient sidecars (e.g.
18
+ * `.config-disassembler.json`) which the original tree never had.
19
+ *
20
+ * For `.xml` files, comparison is **structural and order-insensitive**: sibling elements with the
21
+ * same tag name can appear in any order without registering as drift. Files that are byte-different
22
+ * but semantically equal are returned in `reordered` so the caller can surface the difference.
23
+ */
24
+ export async function diffDirectories(referenceDir, mockDir, prefix = '') {
25
+ const out = { drift: [], reordered: [] };
26
+ let entries;
27
+ try {
28
+ entries = await readdir(referenceDir, { withFileTypes: true });
29
+ }
30
+ catch {
31
+ /* istanbul ignore next -- @preserve: caller already filters to existing directories */
32
+ return out;
33
+ }
34
+ for (const entry of entries) {
35
+ const refPath = join(referenceDir, entry.name);
36
+ const mockPath = join(mockDir, entry.name);
37
+ const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
38
+ if (entry.isDirectory()) {
39
+ // eslint-disable-next-line no-await-in-loop
40
+ const nested = await diffDirectories(refPath, mockPath, relPath);
41
+ out.drift.push(...nested.drift);
42
+ out.reordered.push(...nested.reordered);
43
+ continue;
44
+ }
45
+ // eslint-disable-next-line no-await-in-loop
46
+ const mockExists = await fileExists(mockPath);
47
+ if (!mockExists) {
48
+ out.drift.push({ path: relPath, reason: 'missing in round-trip output' });
49
+ continue;
50
+ }
51
+ // eslint-disable-next-line no-await-in-loop
52
+ const [ref, mock] = await Promise.all([readFile(refPath, 'utf-8'), readFile(mockPath, 'utf-8')]);
53
+ if (ref === mock)
54
+ continue;
55
+ if (isXmlFile(entry.name)) {
56
+ // Byte-different but maybe semantically identical — e.g. siblings reordered on round trip.
57
+ if (xmlEquivalent(ref, mock)) {
58
+ out.reordered.push(relPath);
59
+ }
60
+ else {
61
+ out.drift.push({ path: relPath, reason: 'content drift' });
62
+ }
63
+ }
64
+ else {
65
+ out.drift.push({ path: relPath, reason: 'content drift' });
66
+ }
67
+ }
68
+ return out;
69
+ }
70
+ async function fileExists(path) {
71
+ try {
72
+ await stat(path);
73
+ return true;
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ }
79
+ function isXmlFile(fileName) {
80
+ return extname(fileName).toLowerCase() === '.xml';
81
+ }
82
+ /**
83
+ * Compare two XML strings for structural equality, ignoring sibling order and attribute order.
84
+ * Falls back to `false` if either side fails to parse, so genuinely malformed output still
85
+ * surfaces as drift through the caller.
86
+ */
87
+ export function xmlEquivalent(a, b) {
88
+ if (a === b)
89
+ return true;
90
+ let parsedA;
91
+ let parsedB;
92
+ try {
93
+ parsedA = xmlParser.parse(a);
94
+ parsedB = xmlParser.parse(b);
95
+ }
96
+ catch {
97
+ /* istanbul ignore next -- @preserve: fast-xml-parser is permissive; this is defensive only */
98
+ return false;
99
+ }
100
+ return canonicalJson(parsedA) === canonicalJson(parsedB);
101
+ }
102
+ /**
103
+ * Convert any JSON value into a stable string representation: object keys are sorted, and arrays
104
+ * are sorted by the canonical-JSON of each element. Two values produce the same canonical string
105
+ * iff they are deeply equal up to sibling order.
106
+ */
107
+ export function canonicalJson(value) {
108
+ return JSON.stringify(canonicalize(value));
109
+ }
110
+ function canonicalize(value) {
111
+ if (value === null || typeof value !== 'object')
112
+ return value;
113
+ if (Array.isArray(value)) {
114
+ const normalized = value.map(canonicalize);
115
+ normalized.sort((left, right) => {
116
+ const ls = JSON.stringify(left);
117
+ const rs = JSON.stringify(right);
118
+ if (ls < rs)
119
+ return -1;
120
+ if (ls > rs)
121
+ return 1;
122
+ return 0;
123
+ });
124
+ return normalized;
125
+ }
126
+ const record = value;
127
+ const keys = Object.keys(record).sort();
128
+ const out = {};
129
+ for (const key of keys) {
130
+ out[key] = canonicalize(record[key]);
131
+ }
132
+ return out;
133
+ }
134
+ //# sourceMappingURL=diffDirectories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diffDirectories.js","sourceRoot":"","sources":["../../../src/service/verify/diffDirectories.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ # summary
2
+
3
+ Round-trip verify that decompose followed by recompose preserves your metadata byte-for-byte.
4
+
5
+ # description
6
+
7
+ Copies your package directories into a temp directory under your OS's `tmpdir()`, runs decompose
8
+ then recompose there with the same flags and `.sfdecomposer.config.json` overrides you would use
9
+ in production, then diffs every rebuilt parent XML against the original. Comparison is
10
+ **structural** (sibling and attribute order are ignored, matching how Salesforce treats metadata).
11
+ The command never modifies your working tree.
12
+
13
+ Files where the only delta is ordering are surfaced as informational notices ("reordered") and do
14
+ not fail the run. Genuine semantic differences are reported as drift and exit non-zero, which
15
+ makes the command suitable as a CI gate before committing strategy, format, or override changes.
16
+
17
+ # examples
18
+
19
+ - `sf decomposer verify -m "permissionset" -f "xml"`
20
+ - `sf decomposer verify -m "permissionset" -m "profile" -s "grouped-by-tag" -p`
21
+ - `sf decomposer verify -x "manifest/package.xml" --config`
22
+
23
+ # flags.metadata-type.summary
24
+
25
+ The metadata suffix to verify, such as 'flow', 'labels', etc. Required unless --manifest is provided.
26
+
27
+ # flags.manifest.summary
28
+
29
+ Path to a package.xml manifest file. When provided, only the metadata listed in the manifest is verified. If --metadata-type is also provided, the intersection of the two is used.
30
+
31
+ # flags.format.summary
32
+
33
+ File format to decompose into for the round-trip check.
34
+
35
+ # flags.ignore-package-directory.summary
36
+
37
+ Ignore a package directory.
38
+
39
+ # flags.strategy.summary
40
+
41
+ Strategy to follow when decomposing files for the round-trip check.
42
+
43
+ # flags.decompose-nested-permissions.summary
44
+
45
+ Additionally decompose object and field permissions on a permission set when strategy is set to "grouped-by-tag".
46
+
47
+ # flags.config.summary
48
+
49
+ Load per-type and per-component overrides from .sfdecomposer.config.json in the repo root, the same as `decomposer decompose --config`.
50
+
51
+ # error.missingMetadataOrManifest
52
+
53
+ Either --metadata-type (-m) or --manifest (-x) must be provided.
54
+
55
+ # error.driftDetected
56
+
57
+ Round-trip verify failed: %s file(s) drifted between the original tree and the round-tripped output. See the log above for the offending paths.
@@ -219,7 +219,128 @@
219
219
  "decomposer:recompose",
220
220
  "recompose:decomposer"
221
221
  ]
222
+ },
223
+ "decomposer:verify": {
224
+ "aliases": [],
225
+ "args": {},
226
+ "description": "Copies your package directories into a temp directory under your OS's `tmpdir()`, runs decompose\nthen recompose there with the same flags and `.sfdecomposer.config.json` overrides you would use\nin production, then diffs every rebuilt parent XML against the original. Comparison is\n**structural** (sibling and attribute order are ignored, matching how Salesforce treats metadata).\nThe command never modifies your working tree.\n\nFiles where the only delta is ordering are surfaced as informational notices (\"reordered\") and do\nnot fail the run. Genuine semantic differences are reported as drift and exit non-zero, which\nmakes the command suitable as a CI gate before committing strategy, format, or override changes.",
227
+ "examples": [
228
+ "`sf decomposer verify -m \"permissionset\" -f \"xml\"`",
229
+ "`sf decomposer verify -m \"permissionset\" -m \"profile\" -s \"grouped-by-tag\" -p`",
230
+ "`sf decomposer verify -x \"manifest/package.xml\" --config`"
231
+ ],
232
+ "flags": {
233
+ "json": {
234
+ "description": "Format output as json.",
235
+ "helpGroup": "GLOBAL",
236
+ "name": "json",
237
+ "allowNo": false,
238
+ "type": "boolean"
239
+ },
240
+ "flags-dir": {
241
+ "helpGroup": "GLOBAL",
242
+ "name": "flags-dir",
243
+ "summary": "Import flag values from a directory.",
244
+ "hasDynamicHelp": false,
245
+ "multiple": false,
246
+ "type": "option"
247
+ },
248
+ "metadata-type": {
249
+ "char": "m",
250
+ "name": "metadata-type",
251
+ "required": false,
252
+ "summary": "The metadata suffix to verify, such as 'flow', 'labels', etc. Required unless --manifest is provided.",
253
+ "hasDynamicHelp": false,
254
+ "multiple": true,
255
+ "type": "option"
256
+ },
257
+ "manifest": {
258
+ "char": "x",
259
+ "name": "manifest",
260
+ "required": false,
261
+ "summary": "Path to a package.xml manifest file. When provided, only the metadata listed in the manifest is verified. If --metadata-type is also provided, the intersection of the two is used.",
262
+ "hasDynamicHelp": false,
263
+ "multiple": false,
264
+ "type": "option"
265
+ },
266
+ "format": {
267
+ "char": "f",
268
+ "name": "format",
269
+ "required": true,
270
+ "summary": "File format to decompose into for the round-trip check.",
271
+ "default": "xml",
272
+ "hasDynamicHelp": false,
273
+ "multiple": false,
274
+ "options": [
275
+ "xml",
276
+ "json",
277
+ "yaml",
278
+ "json5"
279
+ ],
280
+ "type": "option"
281
+ },
282
+ "ignore-package-directory": {
283
+ "char": "i",
284
+ "name": "ignore-package-directory",
285
+ "required": false,
286
+ "summary": "Ignore a package directory.",
287
+ "hasDynamicHelp": false,
288
+ "multiple": true,
289
+ "type": "option"
290
+ },
291
+ "strategy": {
292
+ "char": "s",
293
+ "name": "strategy",
294
+ "required": true,
295
+ "summary": "Strategy to follow when decomposing files for the round-trip check.",
296
+ "default": "unique-id",
297
+ "hasDynamicHelp": false,
298
+ "multiple": false,
299
+ "options": [
300
+ "unique-id",
301
+ "grouped-by-tag"
302
+ ],
303
+ "type": "option"
304
+ },
305
+ "decompose-nested-permissions": {
306
+ "char": "p",
307
+ "name": "decompose-nested-permissions",
308
+ "required": false,
309
+ "summary": "Additionally decompose object and field permissions on a permission set when strategy is set to \"grouped-by-tag\".",
310
+ "allowNo": false,
311
+ "type": "boolean"
312
+ },
313
+ "config": {
314
+ "char": "c",
315
+ "name": "config",
316
+ "required": false,
317
+ "summary": "Load per-type and per-component overrides from .sfdecomposer.config.json in the repo root, the same as `decomposer decompose --config`.",
318
+ "allowNo": false,
319
+ "type": "boolean"
320
+ }
321
+ },
322
+ "hasDynamicHelp": false,
323
+ "hiddenAliases": [],
324
+ "id": "decomposer:verify",
325
+ "pluginAlias": "sf-decomposer",
326
+ "pluginName": "sf-decomposer",
327
+ "pluginType": "core",
328
+ "strict": true,
329
+ "summary": "Round-trip verify that decompose followed by recompose preserves your metadata byte-for-byte.",
330
+ "enableJsonFlag": true,
331
+ "isESM": true,
332
+ "relativePath": [
333
+ "lib",
334
+ "commands",
335
+ "decomposer",
336
+ "verify.js"
337
+ ],
338
+ "aliasPermutations": [],
339
+ "permutations": [
340
+ "decomposer:verify",
341
+ "verify:decomposer"
342
+ ]
222
343
  }
223
344
  },
224
- "version": "6.13.0"
345
+ "version": "6.14.0"
225
346
  }
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "sf-decomposer",
3
3
  "description": "Split large Salesforce metadata files into version-control-friendly pieces and rebuild deployment-ready files.",
4
- "version": "6.13.0",
4
+ "version": "6.14.0",
5
5
  "dependencies": {
6
6
  "@oclif/core": "^4",
7
7
  "@salesforce/core": "^8.26.3",
8
8
  "@salesforce/sf-plugins-core": "^12.2.6",
9
9
  "@salesforce/source-deploy-retrieve": "^12.35.0",
10
10
  "config-disassembler": "^1.0.0",
11
+ "fast-xml-parser": "^5.7.2",
11
12
  "p-limit": "^7.3.0"
12
13
  },
13
14
  "devDependencies": {
@@ -143,8 +144,10 @@
143
144
  "files": [
144
145
  "src/commands/decomposer/decompose.ts",
145
146
  "src/commands/decomposer/recompose.ts",
147
+ "src/commands/decomposer/verify.ts",
146
148
  "messages/decomposer.decompose.md",
147
149
  "messages/decomposer.recompose.md",
150
+ "messages/decomposer.verify.md",
148
151
  "README.md"
149
152
  ],
150
153
  "dependencies": [