sf-decomposer 6.33.1 → 6.34.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +16 -2
  2. package/HANDBOOK.md +55 -10
  3. package/README.md +112 -285
  4. package/lib/commands/decomposer/decompose.js +2 -2
  5. package/lib/commands/decomposer/decompose.js.map +1 -1
  6. package/lib/commands/decomposer/recompose.js +1 -1
  7. package/lib/commands/decomposer/recompose.js.map +1 -1
  8. package/lib/commands/decomposer/verify.js +2 -2
  9. package/lib/commands/decomposer/verify.js.map +1 -1
  10. package/lib/core/decomposeMetadataTypes.d.ts +1 -1
  11. package/lib/core/decomposeMetadataTypes.js +5 -5
  12. package/lib/core/decomposeMetadataTypes.js.map +1 -1
  13. package/lib/core/recomposeMetadataTypes.js +2 -2
  14. package/lib/core/recomposeMetadataTypes.js.map +1 -1
  15. package/lib/core/verifyMetadataTypes.js +2 -2
  16. package/lib/core/verifyMetadataTypes.js.map +1 -1
  17. package/lib/helpers/configOverrides.d.ts +13 -0
  18. package/lib/helpers/configOverrides.js +39 -3
  19. package/lib/helpers/configOverrides.js.map +1 -1
  20. package/lib/helpers/types.d.ts +8 -5
  21. package/lib/hooks/prerun.js +2 -2
  22. package/lib/hooks/prerun.js.map +1 -1
  23. package/lib/hooks/scopedPostRetrieve.js +2 -2
  24. package/lib/hooks/scopedPostRetrieve.js.map +1 -1
  25. package/lib/metadata/getPackageDirectories.js +4 -4
  26. package/lib/metadata/getPackageDirectories.js.map +1 -1
  27. package/lib/metadata/getRegistryValuesBySuffix.js +2 -1
  28. package/lib/metadata/getRegistryValuesBySuffix.js.map +1 -1
  29. package/lib/metadata/parseManifest.js +3 -3
  30. package/lib/metadata/parseManifest.js.map +1 -1
  31. package/lib/service/core/getRepoRoot.js +3 -3
  32. package/lib/service/core/getRepoRoot.js.map +1 -1
  33. package/lib/service/core/moveFiles.js +1 -1
  34. package/lib/service/core/moveFiles.js.map +1 -1
  35. package/lib/service/core/updateForceignore.js +1 -1
  36. package/lib/service/core/updateForceignore.js.map +1 -1
  37. package/lib/service/decompose/customLabels.js +2 -2
  38. package/lib/service/decompose/customLabels.js.map +1 -1
  39. package/lib/service/decompose/decomposeFileHandler.d.ts +1 -1
  40. package/lib/service/decompose/decomposeFileHandler.js +11 -5
  41. package/lib/service/decompose/decomposeFileHandler.js.map +1 -1
  42. package/lib/service/decompose/renameWorkflows.js +2 -2
  43. package/lib/service/decompose/renameWorkflows.js.map +1 -1
  44. package/lib/service/recompose/deleteFilesinDirectory.js +1 -1
  45. package/lib/service/recompose/deleteFilesinDirectory.js.map +1 -1
  46. package/lib/service/recompose/recomposeFileHandler.js +1 -1
  47. package/lib/service/recompose/recomposeFileHandler.js.map +1 -1
  48. package/oclif.manifest.json +1 -1
  49. package/package.json +102 -35
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@
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.34.0](https://github.com/mcarvin8/sf-decomposer/compare/v6.33.2...v6.34.0) (2026-06-30)
9
+
10
+
11
+ ### Features
12
+
13
+ * add sidecar elements support for XML decompose/recompose ([#507](https://github.com/mcarvin8/sf-decomposer/issues/507)) ([76fb5a8](https://github.com/mcarvin8/sf-decomposer/commit/76fb5a8ca1293ed1d32213682342af6c52d4599c))
14
+
15
+ ## [6.33.2](https://github.com/mcarvin8/sf-decomposer/compare/v6.33.1...v6.33.2) (2026-06-29)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **deps:** bump all deps, drop Node 20 support ([#505](https://github.com/mcarvin8/sf-decomposer/issues/505)) ([7425c87](https://github.com/mcarvin8/sf-decomposer/commit/7425c87c24721ab8e41938ed6fb85792ef56dff4))
21
+
8
22
  ## [6.33.1](https://github.com/mcarvin8/sf-decomposer/compare/v6.33.0...v6.33.1) (2026-06-25)
9
23
 
10
24
 
@@ -335,8 +349,8 @@ All notable changes to this project will be documented in this file. See [standa
335
349
 
336
350
  ### ⚠ BREAKING CHANGES
337
351
 
338
- * Remove --debug flag and disassemble.log file
339
- * Remove INI and TOML formats from decompose
352
+ * Remove --debug flag and disassemble.log file
353
+ * Remove INI and TOML formats from decompose
340
354
  * Re-decompose files to include updated key config JSONs from rust crate
341
355
 
342
356
  ### Features
package/HANDBOOK.md CHANGED
@@ -12,6 +12,7 @@ If you want the underlying option grammar instead of recipes, see the [main READ
12
12
 
13
13
  - [Choosing a strategy](#choosing-a-strategy)
14
14
  - [Common pitfalls](#common-pitfalls)
15
+ - [ExternalServiceRegistration (ESR / OpenAPI schema)](#externalserviceregistration-esr--openapi-schema)
15
16
  - [Bots (Agentforce and Einstein)](#bots-agentforce-and-einstein)
16
17
  - [Flexipages (Lightning App / Record / Home pages)](#flexipages-lightning-app--record--home-pages)
17
18
  - [Layouts (page layouts)](#layouts-page-layouts)
@@ -23,7 +24,7 @@ If you want the underlying option grammar instead of recipes, see the [main READ
23
24
  Three knobs cover almost every case:
24
25
 
25
26
  | Symptom of the source XML | Reach for |
26
- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
27
+ |-----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|
27
28
  | One repeating section at the top level (e.g. `<labels>` of a custom-labels file). | `strategy: unique-id` (default). |
28
29
  | Lots of small repeatable tags, you want one file per tag-name, not per-instance. | `strategy: grouped-by-tag`. |
29
30
  | `grouped-by-tag`, but a few specific tags (e.g. `objectPermissions`) need finer-grained diff. | Add `splitTags`. |
@@ -31,12 +32,13 @@ Three knobs cover almost every case:
31
32
 
32
33
  Hard rules the plugin always enforces (so you don't have to):
33
34
 
34
- - `labels` and `loyaltyProgramSetup` are always treated as `unique-id` regardless of any override.
35
+ - `labels`, `loyaltyProgramSetup`, and `externalServiceRegistration` are always treated as `unique-id` regardless of any override.
36
+ - `externalServiceRegistration` always applies sidecar extraction for the `<schema>` element (written to `<componentName>.yaml`). No config needed.
35
37
 
36
38
  Built-in `multiLevel` defaults — applied automatically when `strategy` is `unique-id` and you do not supply your own `multiLevel` for that type. You can replace any of them by setting an explicit `multiLevel` on the override.
37
39
 
38
40
  | Metadata type | Built-in `multiLevel` |
39
- | --------------------- | ------------------------------------------------------------------- |
41
+ |-----------------------|---------------------------------------------------------------------|
40
42
  | `bot` | `["botDialogs:botDialogs:developerName", "botSteps:botSteps:type"]` |
41
43
  | `loyaltyProgramSetup` | `"programProcesses:programProcesses:parameterName,ruleName"` |
42
44
 
@@ -64,6 +66,48 @@ There are two distinct causes; run the decompose under `RUST_LOG=warn` to tell t
64
66
  - **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
67
  - **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
68
 
69
+ ## ExternalServiceRegistration (ESR / OpenAPI schema)
70
+
71
+ **Why this is hard.** An `ExternalServiceRegistration` embeds its full OpenAPI (or other) schema document as raw text inside a `<schema>` XML element. This block can be hundreds of lines of YAML or JSON. Storing it inline in the `.externalServiceRegistration-meta.xml` file produces large, noisy diffs whenever the schema changes.
72
+
73
+ **What the plugin does automatically.** No config is needed. The plugin applies these rules to every ESR component:
74
+
75
+ - **Forced `unique-id` strategy** — grouped-by-tag is a no-op for singleton-per-component types and is silently upgraded.
76
+ - **Sidecar extraction** — the `<schema>` element's text content is extracted to `<componentName>.yaml` alongside the decomposed XML shards, and a `.sidecars.json` manifest is written to record the extraction.
77
+ - **Auto-detect on recompose** — recompose reads `.sidecars.json` and reinserts the schema content into `<schema>` before writing the final XML. No recompose flag is needed.
78
+
79
+ **On-disk layout** for `DropboxFileManagerHandler.externalServiceRegistration-meta.xml`:
80
+
81
+ ```
82
+ externalServiceRegistrations/
83
+ └── DropboxFileManagerHandler/
84
+ ├── DropboxFileManagerHandler.yaml ← extracted <schema> content
85
+ ├── .sidecars.json ← sidecar manifest; do not hand-edit
86
+ └── DropboxFileManagerHandler/ ← unique-id decomposed XML shards
87
+ ├── description.externalServiceRegistration-meta.xml
88
+ ├── label.externalServiceRegistration-meta.xml
89
+ ├── schema.externalServiceRegistration-meta.xml ← placeholder (empty text)
90
+ ├── status.externalServiceRegistration-meta.xml
91
+ ├── operations/
92
+ │ └── uploadFile.operations-meta.xml
93
+ └── ...
94
+ ```
95
+
96
+ **Override example.** Only needed if you want a different extension for the schema file (e.g. `json`):
97
+
98
+ ```json
99
+ {
100
+ "overrides": [
101
+ {
102
+ "metadataTypes": ["externalServiceRegistration"],
103
+ "sidecarElements": "schema:json"
104
+ }
105
+ ]
106
+ }
107
+ ```
108
+
109
+ > **Conflict with Salesforce native decomposition.** Salesforce CLI also decomposes `ExternalServiceRegistration` natively. Never use both on the same type in the same project. See [METADATA_SUPPORT.md](./METADATA_SUPPORT.md) for guidance.
110
+
67
111
  ## Bots (Agentforce and Einstein)
68
112
 
69
113
  **Why this is hard.** Agentforce and Einstein bots both ship as `.bot-meta.xml`, but their internals diverge:
@@ -264,13 +308,14 @@ layouts/
264
308
 
265
309
  These follow the same pattern; pick the rules that match your repo's data. None of these are decomposed natively by Salesforce.
266
310
 
267
- | Metadata type | Suggested override |
268
- | ------------------------------------------ | -------------------------------------------------------------------------------------------------- |
269
- | `flow` | `multiLevel: ["actionCalls:actionCalls:name", "decisions:decisions:name", "rules:rules:name"]` |
270
- | `globalValueSet` | `multiLevel: ["customValue:customValue:fullName"]` — handy when value sets have hundreds of picks. |
271
- | `marketingappextension` | `multiLevel: ["activityDefinitions:activityDefinitions:apiName"]` |
272
- | `cmsDeliveryChannel` (and other CMS types) | `strategy: grouped-by-tag` plus `splitTags` for any wide repeatable tag. |
273
- | `dashboard` | `multiLevel: ["components:components:title"]` one file per dashboard widget. |
311
+ | Metadata type | Suggested override |
312
+ |--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
313
+ | `flow` | `multiLevel: ["actionCalls:actionCalls:name", "decisions:decisions:name", "rules:rules:name"]` |
314
+ | `globalValueSet` | `multiLevel: ["customValue:customValue:fullName"]` — handy when value sets have hundreds of picks. |
315
+ | `field` (CustomField) | `multiLevel: "valueSet:valueSetDefinition:fullName"` — splits each picklist value into its own file. Only effective on fields that define picklist values inline; fields referencing a global value set or non-picklist fields are leaf-only and skipped. |
316
+ | `marketingappextension` | `multiLevel: ["activityDefinitions:activityDefinitions:apiName"]` |
317
+ | `cmsDeliveryChannel` (and other CMS types) | `strategy: grouped-by-tag` plus `splitTags` for any wide repeatable tag. |
318
+ | `dashboard` | `multiLevel: ["components:components:title"]` — one file per dashboard widget. |
274
319
 
275
320
  If a metadata type has a single deeply-nested repeatable block, a one-rule `multiLevel` is enough. Reach for the array form only when you have **two or more** distinct nested sections you want addressable on disk.
276
321