sf-decomposer 6.24.1 → 6.26.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,25 @@
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.26.0](https://github.com/mcarvin8/sf-decomposer/compare/v6.25.0...v6.26.0) (2026-06-03)
9
+
10
+
11
+ ### Features
12
+
13
+ * support user-configurable uniqueIdElements in overrides config ([#472](https://github.com/mcarvin8/sf-decomposer/issues/472)) ([6d7ed7d](https://github.com/mcarvin8/sf-decomposer/commit/6d7ed7dec516a77205af6d9304f68e2a97942d83))
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * remove process.chdir from verify; warn on missing manifest components ([#470](https://github.com/mcarvin8/sf-decomposer/issues/470)) ([81d893e](https://github.com/mcarvin8/sf-decomposer/commit/81d893e323016af712d10c6c0cb818bb1fec34af))
19
+
20
+ ## [6.25.0](https://github.com/mcarvin8/sf-decomposer/compare/v6.24.1...v6.25.0) (2026-05-29)
21
+
22
+
23
+ ### Features
24
+
25
+ * **metadata:** bump @salesforce/source-deploy-retrieve ([#467](https://github.com/mcarvin8/sf-decomposer/issues/467)) ([30193f2](https://github.com/mcarvin8/sf-decomposer/commit/30193f2b1a0235e77653d05de28ee76fb0971e92))
26
+
8
27
  ## [6.24.1](https://github.com/mcarvin8/sf-decomposer/compare/v6.24.0...v6.24.1) (2026-05-26)
9
28
 
10
29
 
package/HANDBOOK.md CHANGED
@@ -11,12 +11,12 @@ If you want the underlying option grammar instead of recipes, see the [main READ
11
11
  ## Contents
12
12
 
13
13
  - [Choosing a strategy](#choosing-a-strategy)
14
+ - [Common pitfalls](#common-pitfalls)
14
15
  - [Bots (Agentforce and Einstein)](#bots-agentforce-and-einstein)
15
16
  - [Flexipages (Lightning App / Record / Home pages)](#flexipages-lightning-app--record--home-pages)
16
17
  - [Layouts (page layouts)](#layouts-page-layouts)
17
18
  - [Other deeply-nested types](#other-deeply-nested-types)
18
19
  - [The verification workflow](#the-verification-workflow)
19
- - [Common pitfalls](#common-pitfalls)
20
20
 
21
21
  ## Choosing a strategy
22
22
 
@@ -44,6 +44,26 @@ The full registry lives in [`src/metadata/multiLevelDefaults.ts`](./src/metadata
44
44
 
45
45
  Everything else in this handbook is opt-in.
46
46
 
47
+ ## Common pitfalls
48
+
49
+ **1. "I added two `multiLevel` rules but only one survived."**
50
+ You probably ran two `decompose` invocations back-to-back, one rule each. Don't. The disassembler rewrites `.multi_level.json` on every run, so each call replaces the prior one. Pass every rule for a given component in **one** override entry, in array form.
51
+
52
+ **2. "My `multiLevel` rule is correct but recompose produces a smaller file."**
53
+ On `config-disassembler` Rust ≥ 0.5.0 / Node ≥ 1.3.0 this should not happen — sibling collisions are written to per-element SHA-256 shards and surfaced as a `WARN` (see pitfall #5), not silently overwritten. If you do see a shrunken recomposed file on a current build, treat it as a regression worth capturing as a fixture.
54
+
55
+ **3. "Component-scope override fields look ignored."**
56
+ Component-scope wins over type-scope, but only for fields **the component override explicitly sets**. Fields it leaves out fall through to the type-scope value, then to the run-wide default. If you set `decomposedFormat: "yaml"` on a type and `strategy: "grouped-by-tag"` on the component, the component still gets `decomposedFormat: "yaml"` from the type override.
57
+
58
+ **4. "Reassembly removed my decomposed directory even though I didn't pass `postPurge`."**
59
+ That's by design for `multiLevel` types only. Multi-level recompose has to clean up inner-level directories so the next level can merge their reassembled XML. If you want the decomposed tree preserved for inspection, copy it before running `recompose`.
60
+
61
+ **5. "Decompose succeeded but my decomposed files all have hash names."**
62
+ There are two distinct causes; run the decompose under `RUST_LOG=warn` to tell them apart:
63
+
64
+ - **No `WARN` line.** Your `unique_id_elements` (or the rule's third part) didn't resolve to a non-empty value on those items. Check the source XML for the elements you listed — names are case-sensitive and live at the immediate child level of each repeating item. The plugin only walks one level deep when picking a UID.
65
+ - **A `WARN` line of the form `uniqueIdElements collision: <parentTag> id "X" matched N sibling elements`.** The configured key is too narrow — multiple siblings legitimately share the same value, so the collision detector falls back to per-element SHA-256 hashes for that group rather than overwrite. Add a tiebreaker to `unique_id_elements` (e.g. a compound like `name+recordType`) and re-decompose with `prePurge: true`.
66
+
47
67
  ## Bots (Agentforce and Einstein)
48
68
 
49
69
  **Why this is hard.** Agentforce and Einstein bots both ship as `.bot-meta.xml`, but their internals diverge:
@@ -263,37 +283,17 @@ Always verify a new override before committing it:
263
283
  git stash --include-untracked
264
284
 
265
285
  # 2. Decompose with the new override.
266
- sf decomposer decompose -t bot --config
286
+ sf decomposer decompose -m "bot" --config
267
287
 
268
288
  # 3. Recompose back from the decomposed tree.
269
- sf decomposer recompose -t bot
289
+ sf decomposer recompose -m "bot"
270
290
 
271
291
  # 4. Check the round-trip didn't drift.
272
- sf decomposer verify -t bot --config
292
+ sf decomposer verify -m "bot" --config
273
293
  ```
274
294
 
275
295
  `sf decomposer verify` is non-destructive: it decomposes into a temp dir, recomposes from the temp dir, and compares the result to your committed source. If anything drifts (content, missing file, sibling reorder) it tells you exactly which paths broke. Treat any drift as a blocker — fix the override (or fall back to the previous one) before committing.
276
296
 
277
- ## Common pitfalls
278
-
279
- **1. "I added two `multiLevel` rules but only one survived."**
280
- You probably ran two `decompose` invocations back-to-back, one rule each. Don't. The disassembler rewrites `.multi_level.json` on every run, so each call replaces the prior one. Pass every rule for a given component in **one** override entry, in array form.
281
-
282
- **2. "My `multiLevel` rule is correct but recompose produces a smaller file."**
283
- On `config-disassembler` Rust ≥ 0.5.0 / Node ≥ 1.3.0 this should not happen — sibling collisions are written to per-element SHA-256 shards and surfaced as a `WARN` (see pitfall #5), not silently overwritten. If you do see a shrunken recomposed file on a current build, treat it as a regression worth capturing as a fixture.
284
-
285
- **3. "Component-scope override fields look ignored."**
286
- Component-scope wins over type-scope, but only for fields **the component override explicitly sets**. Fields it leaves out fall through to the type-scope value, then to the run-wide default. If you set `decomposedFormat: "yaml"` on a type and `strategy: "grouped-by-tag"` on the component, the component still gets `decomposedFormat: "yaml"` from the type override.
287
-
288
- **4. "Reassembly removed my decomposed directory even though I didn't pass `postPurge`."**
289
- That's by design for `multiLevel` types only. Multi-level recompose has to clean up inner-level directories so the next level can merge their reassembled XML. If you want the decomposed tree preserved for inspection, copy it before running `recompose`.
290
-
291
- **5. "Decompose succeeded but my decomposed files all have hash names."**
292
- There are two distinct causes; run the decompose under `RUST_LOG=warn` to tell them apart:
293
-
294
- - **No `WARN` line.** Your `unique_id_elements` (or the rule's third part) didn't resolve to a non-empty value on those items. Check the source XML for the elements you listed — names are case-sensitive and live at the immediate child level of each repeating item. The plugin only walks one level deep when picking a UID.
295
- - **A `WARN` line of the form `uniqueIdElements collision: <parentTag> id "X" matched N sibling elements`.** The configured key is too narrow — multiple siblings legitimately share the same value, so the collision detector falls back to per-element SHA-256 hashes for that group rather than overwrite. Add a tiebreaker to `unique_id_elements` (e.g. a compound like `name+recordType`) and re-decompose with `prePurge: true`.
296
-
297
297
  ---
298
298
 
299
299
  When in doubt: write the override, run `verify`, read the diff. Every recipe in this handbook was derived that way.