sfdx-git-delta 7.4.0 → 7.4.2

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 (71) hide show
  1. package/README.md +22 -3
  2. package/lib/adapter/GitAdapter.d.ts +5 -3
  3. package/lib/adapter/GitAdapter.js +71 -32
  4. package/lib/adapter/GitAdapter.js.map +1 -1
  5. package/lib/adapter/pathMatching.d.ts +2 -1
  6. package/lib/adapter/pathMatching.js +35 -3
  7. package/lib/adapter/pathMatching.js.map +1 -1
  8. package/lib/adapter/treeReader.d.ts +1 -0
  9. package/lib/adapter/treeReader.js +10 -0
  10. package/lib/adapter/treeReader.js.map +1 -1
  11. package/lib/adapter/tsgitErrorMap.d.ts +5 -4
  12. package/lib/adapter/tsgitErrorMap.js +12 -5
  13. package/lib/adapter/tsgitErrorMap.js.map +1 -1
  14. package/lib/constant/metadataConstants.d.ts +12 -1
  15. package/lib/constant/metadataConstants.js +28 -3
  16. package/lib/constant/metadataConstants.js.map +1 -1
  17. package/lib/main.js +66 -63
  18. package/lib/main.js.map +1 -1
  19. package/lib/metadata/MetadataRepositoryImpl.d.ts +14 -2
  20. package/lib/metadata/MetadataRepositoryImpl.js +192 -50
  21. package/lib/metadata/MetadataRepositoryImpl.js.map +1 -1
  22. package/lib/post-processor/changesManifestProcessor.js +14 -1
  23. package/lib/post-processor/changesManifestProcessor.js.map +1 -1
  24. package/lib/post-processor/includeProcessor.js +8 -9
  25. package/lib/post-processor/includeProcessor.js.map +1 -1
  26. package/lib/service/botHandler.js +1 -1
  27. package/lib/service/botHandler.js.map +1 -1
  28. package/lib/service/inBundleHandler.js +10 -23
  29. package/lib/service/inBundleHandler.js.map +1 -1
  30. package/lib/service/inFolderHandler.js +1 -2
  31. package/lib/service/inFolderHandler.js.map +1 -1
  32. package/lib/service/objectTranslationHandler.d.ts +0 -1
  33. package/lib/service/objectTranslationHandler.js +11 -4
  34. package/lib/service/objectTranslationHandler.js.map +1 -1
  35. package/lib/types/runContext.d.ts +16 -5
  36. package/lib/types/runContext.js +17 -5
  37. package/lib/types/runContext.js.map +1 -1
  38. package/lib/utils/__mocks__/LoggingService.d.ts +2 -7
  39. package/lib/utils/__mocks__/LoggingService.js.map +1 -1
  40. package/lib/utils/changeSet.d.ts +4 -0
  41. package/lib/utils/changeSet.js +87 -37
  42. package/lib/utils/changeSet.js.map +1 -1
  43. package/lib/utils/configValidator.d.ts +11 -1
  44. package/lib/utils/configValidator.js +133 -80
  45. package/lib/utils/configValidator.js.map +1 -1
  46. package/lib/utils/errorUtils.d.ts +5 -0
  47. package/lib/utils/errorUtils.js +8 -0
  48. package/lib/utils/errorUtils.js.map +1 -1
  49. package/lib/utils/messageSanitizer.d.ts +1 -0
  50. package/lib/utils/messageSanitizer.js +46 -0
  51. package/lib/utils/messageSanitizer.js.map +1 -1
  52. package/lib/utils/metadataBoundaryResolver.d.ts +4 -0
  53. package/lib/utils/metadataBoundaryResolver.js +40 -29
  54. package/lib/utils/metadataBoundaryResolver.js.map +1 -1
  55. package/lib/utils/metadataDiff/deepEqualJson.js +51 -44
  56. package/lib/utils/metadataDiff/deepEqualJson.js.map +1 -1
  57. package/lib/utils/metadataDiff/xmlEventReader.js +60 -50
  58. package/lib/utils/metadataDiff/xmlEventReader.js.map +1 -1
  59. package/lib/utils/repoGitDiff.d.ts +20 -2
  60. package/lib/utils/repoGitDiff.js +129 -35
  61. package/lib/utils/repoGitDiff.js.map +1 -1
  62. package/lib/utils/treeIndexScope.js +25 -37
  63. package/lib/utils/treeIndexScope.js.map +1 -1
  64. package/lib/utils/treeReaderBuilder.d.ts +8 -0
  65. package/lib/utils/treeReaderBuilder.js +34 -0
  66. package/lib/utils/treeReaderBuilder.js.map +1 -0
  67. package/lib/utils/txmlAdapter.js +1 -1
  68. package/lib/utils/txmlAdapter.js.map +1 -1
  69. package/messages/delta.md +16 -5
  70. package/oclif.manifest.json +1 -1
  71. package/package.json +90 -49
@@ -1,4 +1,14 @@
1
1
  'use strict';
2
+ export const BOT_TYPE = 'Bot';
3
+ export const BOT_VERSION_TYPE = 'BotVersion';
4
+ export const CUSTOM_APPLICATION_SUFFIX = 'app';
5
+ export const CUSTOM_METADATA_SUFFIX = 'md';
6
+ export const DASHBOARD_TYPE = 'Dashboard';
7
+ // A component named by the single segment below its type directory: every
8
+ // content-container adapter except digitalExperience (depths below), and the
9
+ // holder-scoped composed types, whose every file keys on that one segment.
10
+ export const DEFAULT_CONTAINER_DEPTH = 1;
11
+ export const DIGITAL_EXPERIENCE_ADAPTER = 'digitalExperience';
2
12
  // SDR adapters whose component is a directory owning an opaque content subtree
3
13
  // (StaticResource/ExperienceBundle/Document via mixedContent, Aura/LWC via
4
14
  // bundle, DigitalExperienceBundle via digitalExperience). When such a type is
@@ -6,18 +16,22 @@
6
16
  // metadata directories — so type resolution must stop there.
7
17
  export const CONTENT_CONTAINER_ADAPTERS = new Set([
8
18
  'bundle',
9
- 'digitalExperience',
19
+ DIGITAL_EXPERIENCE_ADAPTER,
10
20
  'mixedContent',
11
21
  ]);
12
- export const CUSTOM_APPLICATION_SUFFIX = 'app';
13
- export const CUSTOM_METADATA_SUFFIX = 'md';
14
22
  export const DIGITAL_EXPERIENCE_BUNDLE_TYPE = 'DigitalExperienceBundle';
23
+ // SDR's canonical layout below `digitalExperiences/` is
24
+ // `<baseType>/<spaceApiName>/<contentType>/<contentApiName>/<file>`: the
25
+ // bundle is the first two segments, a page content folder the first four.
26
+ export const DIGITAL_EXPERIENCE_BUNDLE_DEPTH = 2;
27
+ export const DIGITAL_EXPERIENCE_CONTENT_DEPTH = 4;
15
28
  export const DIGITAL_EXPERIENCE_TYPE = 'DigitalExperience';
16
29
  export const EMAIL_SERVICES_FUNCTION_SUFFIX = 'xml';
17
30
  export const FIELD_DIRECTORY_NAME = 'fields';
18
31
  export const FLOW_DEFINITIONS_KEY = 'flowDefinitions';
19
32
  export const FLOW_XML_NAME = 'Flow';
20
33
  export const INFOLDER_SUFFIX = `Folder`;
34
+ export const INFOLDER_SUFFIX_REGEX = new RegExp(`${INFOLDER_SUFFIX}$`);
21
35
  export const LABEL_DECOMPOSED_SUFFIX = 'label';
22
36
  export const MASTER_DETAIL_TAG = '<type>MasterDetail</type>';
23
37
  export const METAFILE_SUFFIX = '-meta.xml';
@@ -26,6 +40,16 @@ export const OBJECT_TRANSLATION_META_XML_SUFFIX = `objectTranslation${METAFILE_S
26
40
  export const OBJECT_TRANSLATION_TYPE = 'CustomObjectTranslation';
27
41
  export const OBJECT_TYPE = 'CustomObject';
28
42
  export const PERMISSIONSET_TYPE = 'PermissionSet';
43
+ export const REPORT_TYPE = 'Report';
44
+ // The Metadata API relocates these when the package lists them under a new
45
+ // folder, so their former path must not be destroyed alongside the move.
46
+ // Declared out of alphabetical order on purpose: the Set dereferences both
47
+ // members at module evaluation, so filing it under F would read REPORT_TYPE
48
+ // before its initialiser has run.
49
+ export const FOLDER_MOVE_ON_DEPLOY_TYPES = new Set([
50
+ REPORT_TYPE,
51
+ DASHBOARD_TYPE,
52
+ ]);
29
53
  export const SHARING_RULE_TYPE = 'SharingRules';
30
54
  export const SUB_OBJECT_TYPES = [
31
55
  'BusinessProcess',
@@ -54,5 +78,6 @@ export const SUB_OBJECT_TYPES = [
54
78
  ];
55
79
  export const TRANSLATION_EXTENSION = 'translation';
56
80
  export const TRANSLATION_TYPE = 'Translations';
81
+ export const VIRTUAL_BOT_TYPE = 'VirtualBot';
57
82
  export const WORKFLOW_TYPE = 'Workflow';
58
83
  //# sourceMappingURL=metadataConstants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"metadataConstants.js","sourceRoot":"","sources":["../../src/constant/metadataConstants.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AACZ,+EAA+E;AAC/E,2EAA2E;AAC3E,8EAA8E;AAC9E,+EAA+E;AAC/E,6DAA6D;AAC7D,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAC;IAChD,QAAQ;IACR,mBAAmB;IACnB,cAAc;CACf,CAAC,CAAA;AACF,MAAM,CAAC,MAAM,yBAAyB,GAAG,KAAK,CAAA;AAC9C,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAA;AAC1C,MAAM,CAAC,MAAM,8BAA8B,GAAG,yBAAyB,CAAA;AACvE,MAAM,CAAC,MAAM,uBAAuB,GAAG,mBAAmB,CAAA;AAC1D,MAAM,CAAC,MAAM,8BAA8B,GAAG,KAAK,CAAA;AACnD,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAA;AAC5C,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAA;AACrD,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAA;AACnC,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAA;AACvC,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAA;AAC9C,MAAM,CAAC,MAAM,iBAAiB,GAAG,2BAA2B,CAAA;AAC5D,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAA;AAC1C,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,GAAG,eAAe,GAAG,CAAC,CAAA;AAC3D,MAAM,CAAC,MAAM,kCAAkC,GAAG,oBAAoB,eAAe,EAAE,CAAA;AACvF,MAAM,CAAC,MAAM,uBAAuB,GAAG,yBAAyB,CAAA;AAChE,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAA;AACzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAA;AACjD,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAA;AAC/C,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,iBAAiB;IACjB,eAAe;IACf,aAAa;IACb,UAAU;IACV,OAAO;IACP,UAAU;IACV,YAAY;IACZ,qBAAqB;IACrB,kBAAkB;IAClB,kBAAkB;IAClB,eAAe;IACf,YAAY;IACZ,gBAAgB;IAChB,gBAAgB;IAChB,SAAS;IACT,eAAe;IACf,qBAAqB;IACrB,oBAAoB;IACpB,0BAA0B;IAC1B,yBAAyB;IACzB,cAAc;IACd,cAAc;IACd,cAAc;CACf,CAAA;AACD,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAA;AAClD,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAc,CAAA;AAC9C,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAA"}
1
+ {"version":3,"file":"metadataConstants.js","sourceRoot":"","sources":["../../src/constant/metadataConstants.ts"],"names":[],"mappings":"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"}
package/lib/main.js CHANGED
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
  import GitAdapter from './adapter/GitAdapter.js';
3
3
  import IOExecutor from './adapter/ioExecutor.js';
4
- import { createTreeReader, EMPTY_TREE_READER } from './adapter/treeReader.js';
5
4
  import { getDefinition } from './metadata/metadataManager.js';
6
5
  import { getPostProcessors } from './post-processor/postProcessorManager.js';
7
6
  import DiffLineInterpreter from './service/diffLineInterpreter.js';
@@ -15,6 +14,14 @@ import { parseSourceDirs } from './utils/pathspec.js';
15
14
  import RenameResolver from './utils/renameResolver.js';
16
15
  import RepoGitDiff from './utils/repoGitDiff.js';
17
16
  import { computeTreeIndexScope } from './utils/treeIndexScope.js';
17
+ import { buildRunTreeReader } from './utils/treeReaderBuilder.js';
18
+ const collectLines = async (lines) => {
19
+ const materialized = [];
20
+ for await (const line of lines) {
21
+ materialized.push(line);
22
+ }
23
+ return materialized;
24
+ };
18
25
  export default async (configInput) => {
19
26
  // Stryker disable next-line StringLiteral -- equivalent: log content is observability only; tests assert on the returned Work, not lazy log lines
20
27
  Logger.trace('main: entry');
@@ -30,55 +37,28 @@ export default async (configInput) => {
30
37
  const configWarnings = await new ConfigValidator(config, rejections).validateConfig();
31
38
  const metadata = await getDefinition(config);
32
39
  const repoGitDiffHelper = new RepoGitDiff(config, metadata);
33
- // The treeIndex scope read needs the full path set up front, so when
34
- // generateDelta is on without an include override we materialize the
35
- // streamed diff once. Otherwise we feed the async iterable straight
36
- // into DiffLineInterpreter so handlers start firing while git is
37
- // still emitting lines.
38
- const needsScopeFromDiff = config.generateDelta && !config.include && !config.includeDestructive;
39
- let lines;
40
- if (needsScopeFromDiff) {
41
- const materialized = [];
42
- for await (const line of repoGitDiffHelper.getLines()) {
43
- materialized.push(line);
44
- }
45
- lines = materialized;
46
- }
47
- // Stryker disable next-line BlockStatement -- equivalent: emptying the else block leaves `lines` undefined which downstream lineProcessor.process(undefined) iterates as empty; the test main.test.ts only asserts mockProcess was called with the materialized array in the if-branch fixture, and the else-body BlockStatement mutant on the assignment expression is observably equivalent because mockProcess accepts undefined under stryker's perTest path-coverage analysis
48
- else {
49
- lines = repoGitDiffHelper.getLines();
50
- }
51
- // Built here and threaded to every reader (DiffLineInterpreter,
52
- // RenameResolver, IOExecutor, the post-processors) rather than cached
53
- // on GitAdapter: one TreeIndex per revision, for this run only, so a
54
- // reader can never see a different scope than the one this block just
55
- // built under — the lesson from the shared, scope-keyed cache this
56
- // replaces (see design history) is that a cache keyed by a value each
57
- // reader recomputes is an implicit contract that will drift.
58
- let trees = EMPTY_TREE_READER;
59
- if (config.generateDelta) {
60
- const gitAdapter = GitAdapter.getInstance(config);
61
- let scopePaths = config.source;
62
- if (needsScopeFromDiff) {
63
- scopePaths = [
64
- ...computeTreeIndexScope(lines, metadata),
65
- ];
66
- }
67
- if (scopePaths.length > 0) {
68
- const [toIndex, fromIndex] = await Promise.all([
69
- gitAdapter.buildTreeIndex(config.to, scopePaths),
70
- gitAdapter.buildTreeIndex(config.from, scopePaths),
71
- ]);
72
- const entries = new Map();
73
- // Stryker disable ConditionalExpression -- equivalent: forcing either guard to always run stores `undefined` at that revision key instead of skipping it, but the only reader is createTreeReader's `entries.get(revision) ?? NO_INDEX`, and Map.get answers `undefined` for an absent key and an explicitly-undefined value alike. `entries` is local to this block and exposes no has/size/enumeration, so no observer can tell the two apart
74
- if (toIndex)
75
- entries.set(config.to, toIndex);
76
- if (fromIndex)
77
- entries.set(config.from, fromIndex);
78
- // Stryker restore ConditionalExpression
79
- trees = createTreeReader(entries);
80
- }
81
- }
40
+ // The tree-index scope is read off the diff whenever no include file
41
+ // overrides it, so the stream is materialised once: computeTreeIndexScope
42
+ // and lineProcessor.process walk the same lines. With an include file the
43
+ // scope is config.source and the async iterable feeds straight into
44
+ // DiffLineInterpreter, so handlers fire while git is still emitting.
45
+ const needsScopeFromDiff = !config.include && !config.includeDestructive;
46
+ const materialized = needsScopeFromDiff
47
+ ? await collectLines(repoGitDiffHelper.getLines())
48
+ : undefined;
49
+ const lines = materialized ?? repoGitDiffHelper.getLines();
50
+ // The manifests must not depend on --generate-delta: every index-backed
51
+ // read that decides package.xml vs destructiveChanges.xml (container
52
+ // liveness, decomposed-holder liveness, the include listing) runs in
53
+ // every mode, so the run's two indexes are built in every mode. The flag
54
+ // gates only what is copied. Built here and threaded to every reader
55
+ // (DiffLineInterpreter, RenameResolver, IOExecutor, the post-processors)
56
+ // through RunContext rather than cached on GitAdapter, so no reader can
57
+ // see a different scope than the one this run built under.
58
+ const scopePaths = materialized
59
+ ? [...computeTreeIndexScope(materialized, metadata)]
60
+ : config.source;
61
+ const { trees, unindexed } = await buildRunTreeReader(GitAdapter.getInstance(config), config, scopePaths);
82
62
  const ctx = { config, metadata, trees };
83
63
  const lineProcessor = new DiffLineInterpreter(ctx);
84
64
  const postProcessors = getPostProcessors(ctx);
@@ -102,20 +82,41 @@ export default async (configInput) => {
102
82
  // changes at all — a non-empty manifest (e.g. members sourced by
103
83
  // --include-file independent of the diff) means the scope did its
104
84
  // job, so naming it as unmatched would be misleading.
85
+ const messages = new MessageService();
86
+ // tokens is a thunk, not a precomputed array: sanitizeForMessage must
87
+ // only run when the warning actually fires, matching the ternaries this
88
+ // replaces (some callers never set config.to/from, so an unconditional
89
+ // sanitizeForMessage(requestedTo) would crash on undefined).
90
+ const warnIf = (condition, key, tokens) => condition ? [new Error(messages.getMessage(key, tokens()))] : [];
105
91
  const unmatchedScopes = repoGitDiffHelper.getUnmatchedSourceScopes();
106
- const sourceScopeWarnings = unmatchedScopes.length > 0 && changes.isEmpty()
107
- ? [
108
- new Error(new MessageService().getMessage('warning.SourceDirMatchedNothing', [
109
- // Sanitize each scope before joining: the length cap in
110
- // sanitizeForMessage must apply per scope, not to the
111
- // joined aggregate, or one long scope name silently
112
- // elides every scope listed after it.
113
- unmatchedScopes.map(sanitizeForMessage).join(', '),
114
- sanitizeForMessage(requestedFrom),
115
- sanitizeForMessage(requestedTo),
116
- ])),
117
- ]
118
- : [];
92
+ const sourceScopeWarnings = warnIf(unmatchedScopes.length > 0 && changes.isEmpty(), 'warning.SourceDirMatchedNothing', () => [
93
+ // Sanitize each scope before joining: the length cap in
94
+ // sanitizeForMessage must apply per scope, not to the
95
+ // joined aggregate, or one long scope name silently
96
+ // elides every scope listed after it.
97
+ unmatchedScopes.map(sanitizeForMessage).join(', '),
98
+ sanitizeForMessage(requestedFrom),
99
+ sanitizeForMessage(requestedTo),
100
+ ]);
101
+ // Also final by this point. When the `to` listing could not be read,
102
+ // components moved into an ignored directory were not recognised as
103
+ // moves, so their deletions are reported destructively. That is the
104
+ // safe direction, but it is a degraded answer and must not be silent.
105
+ const probeFailure = repoGitDiffHelper.getHeldAdditionProbeFailure();
106
+ const ignoredMoveWarnings = warnIf(!!probeFailure, 'warning.IgnoredMoveCheckSkipped',
107
+ // Non-null: this thunk only runs when warnIf's condition (!!probeFailure)
108
+ // is true, but the closure sits outside TS's narrowing of that check.
109
+ () => [
110
+ sanitizeForMessage(requestedTo),
111
+ probeFailure.candidateCount.toString(),
112
+ ]);
113
+ // Known as soon as the tree reader is built, but raised here with the
114
+ // rest of the warnings for a single review point. Only `to` is
115
+ // warn-worthy: it is the revision every liveness read (pathExists,
116
+ // readDirs) resolves through, so its degrade is what lets a live
117
+ // container land in destructiveChanges.xml. `from` only feeds deep-path
118
+ // member-name resolution, a narrower and already-attributed effect.
119
+ const treeIndexWarnings = warnIf(unindexed.includes(config.to), 'warning.TreeIndexUnavailable', () => [sanitizeForMessage(requestedTo)]);
119
120
  const work = {
120
121
  config,
121
122
  changes,
@@ -124,6 +125,8 @@ export default async (configInput) => {
124
125
  ...assemblyWarnings,
125
126
  ...processorWarnings,
126
127
  ...sourceScopeWarnings,
128
+ ...ignoredMoveWarnings,
129
+ ...treeIndexWarnings,
127
130
  ],
128
131
  };
129
132
  // Stryker disable next-line StringLiteral -- equivalent: log content is observability only
package/lib/main.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
@@ -6,21 +6,33 @@ export declare class MetadataRepositoryImpl implements MetadataRepository {
6
6
  protected readonly metadataPerDir: Map<string, Metadata>;
7
7
  protected readonly metadataPerXmlName: Map<string, Metadata>;
8
8
  private readonly pathCache;
9
+ private readonly unsafeExtensions;
9
10
  constructor(metadatas: Metadata[]);
10
11
  protected addSuffix(metadata: Metadata): void;
12
+ private registerSuffix;
11
13
  protected addSharedFolderSuffix(metadata: Metadata): void;
12
14
  protected addFolder(metadata: Metadata): void;
13
15
  protected addXmlName(metadata: Metadata): void;
14
16
  has(path: string): boolean;
15
17
  get(path: string): Metadata | undefined;
18
+ private resolve;
16
19
  protected searchByExtension(parts: string[]): Metadata | undefined;
17
20
  protected searchByDirectory(parts: string[]): Metadata | undefined;
18
21
  protected ownsNestedPaths(metadata: Metadata): boolean;
22
+ private isContentContainer;
19
23
  private declaresNestedContent;
20
- protected searchByXmlName(xmlName: string): Metadata | undefined;
21
24
  getByXmlName(xmlName: string): Metadata | undefined;
22
25
  getFullyQualifiedName(path: string): string;
26
+ private plainTypeName;
27
+ private composedTypeName;
28
+ private holderScopedName;
29
+ private componentScopedName;
30
+ private folderScopedName;
31
+ private nestedContentName;
32
+ private containerName;
33
+ private containerDepth;
23
34
  values(): Metadata[];
24
- private static UNSAFE_EXTENSION;
25
35
  private static COMPOSED_TYPES;
36
+ private static HOLDER_SCOPED_COMPOSED_TYPES;
37
+ private static FOLDER_SCOPED_TYPES;
26
38
  }
@@ -6,23 +6,41 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
7
  };
8
8
  import { parse } from 'node:path/posix';
9
- import { DOT, PATH_SEP } from '../constant/fsConstants.js';
10
- import { CONTENT_CONTAINER_ADAPTERS, CUSTOM_APPLICATION_SUFFIX, CUSTOM_METADATA_SUFFIX, EMAIL_SERVICES_FUNCTION_SUFFIX, METAFILE_SUFFIX, OBJECT_TRANSLATION_TYPE, OBJECT_TYPE, PERMISSIONSET_TYPE, SHARING_RULE_TYPE, SUB_OBJECT_TYPES, WORKFLOW_TYPE, } from '../constant/metadataConstants.js';
9
+ import { DOT, EXTENSION_SUFFIX_REGEX, PATH_SEP, } from '../constant/fsConstants.js';
10
+ import { GIT_DIFF_TYPE_REGEX } from '../constant/gitConstants.js';
11
+ import { CONTENT_CONTAINER_ADAPTERS, CUSTOM_APPLICATION_SUFFIX, CUSTOM_METADATA_SUFFIX, DEFAULT_CONTAINER_DEPTH, DIGITAL_EXPERIENCE_ADAPTER, DIGITAL_EXPERIENCE_BUNDLE_DEPTH, DIGITAL_EXPERIENCE_CONTENT_DEPTH, EMAIL_SERVICES_FUNCTION_SUFFIX, INFOLDER_SUFFIX_REGEX, META_REGEX, METAFILE_SUFFIX, OBJECT_TRANSLATION_TYPE, OBJECT_TYPE, PERMISSIONSET_TYPE, SHARING_RULE_TYPE, SUB_OBJECT_TYPES, VIRTUAL_BOT_TYPE, WORKFLOW_TYPE, } from '../constant/metadataConstants.js';
11
12
  import { log } from '../utils/LoggingDecorator.js';
12
- const PATH_SEP_GLOBAL = new RegExp(PATH_SEP, 'g');
13
+ // Callers hand over a git diff line as readily as a bare path, and the
14
+ // status prefix rides on the first segment — which is exactly where a
15
+ // repository laid out at its own root keeps the type directory — so nothing
16
+ // may compare segments before this has run.
17
+ const asFilePath = (path) => path.replace(GIT_DIFF_TYPE_REGEX, '');
18
+ // Shared by every scoped-name arm that owns a metaFile companion, so the
19
+ // `-meta.xml` strip is a single call site both `containerName` and
20
+ // `folderScopedName` route through.
21
+ const withoutMetaSuffix = (name) => name.replace(META_REGEX, '');
13
22
  export class MetadataRepositoryImpl {
14
23
  metadatas;
15
24
  metadataPerExt;
16
25
  metadataPerDir;
17
26
  metadataPerXmlName;
18
27
  // Memoizes get(path) results across the registry's lifetime. The lookup
19
- // chain (split + extension + directory walk + xmlName) is deterministic
20
- // in `path` and the registry is read-only after construction, so a
21
- // single cache here is safe and frees every consumer (has,
22
- // getFullyQualifiedName, TypeHandlerFactory, computeTreeIndexScope, the
23
- // RepoGitDiff filter chain) from repeating the work. Stores `undefined`
24
- // negatives too — distinguished from "uncached" via .has().
28
+ // chain (split + extension + directory walk) is deterministic in `path`
29
+ // and the registry is read-only after construction, so a single cache
30
+ // here is safe and frees every consumer (has, getFullyQualifiedName,
31
+ // TypeHandlerFactory, computeTreeIndexScope, the RepoGitDiff filter
32
+ // chain) from repeating the work. Stores `undefined` negatives too —
33
+ // distinguished from "uncached" via .has().
25
34
  pathCache = new Map();
35
+ // A suffix declared by more than one type is ambiguous for this registry
36
+ // only. Kept per instance rather than shared across the process, so a
37
+ // registry built from an additional user-supplied file cannot make an
38
+ // extension unsafe for every registry built after it.
39
+ unsafeExtensions = new Set([
40
+ CUSTOM_APPLICATION_SUFFIX,
41
+ EMAIL_SERVICES_FUNCTION_SUFFIX,
42
+ CUSTOM_METADATA_SUFFIX,
43
+ ]);
26
44
  constructor(metadatas) {
27
45
  this.metadatas = metadatas;
28
46
  this.metadataPerExt = new Map();
@@ -35,24 +53,36 @@ export class MetadataRepositoryImpl {
35
53
  });
36
54
  }
37
55
  addSuffix(metadata) {
38
- // Stryker disable next-line ConditionalExpression -- equivalent: suffix presence guard; flipping to true runs the inner Map.has lookup on undefined which returns false, falling into the metadataPerExt.set with undefined key — observable as a stray entry that no test path queries by undefined suffix
39
- if (metadata.suffix) {
40
- if (this.metadataPerExt.has(metadata.suffix)) {
41
- MetadataRepositoryImpl.UNSAFE_EXTENSION.add(metadata.suffix);
42
- }
43
- else {
44
- this.metadataPerExt.set(metadata.suffix, metadata);
45
- }
46
- }
56
+ this.registerSuffix(metadata);
47
57
  this.addSharedFolderSuffix(metadata);
48
58
  }
59
+ // Content clones must register through here, not through addSuffix: a clone
60
+ // keeps the same content[] as its source (see addSharedFolderSuffix below),
61
+ // so routing it back through addSuffix would re-enter addSharedFolderSuffix
62
+ // on that same content[] and recurse forever.
63
+ registerSuffix(metadata) {
64
+ // Stryker disable next-line ConditionalExpression -- equivalent: suffix presence guard; flipping to false lets a suffix-less entry reach the Map.has lookup on undefined, which returns false and falls into metadataPerExt.set under an undefined key — a stray entry no test path queries by undefined suffix (the true flip registers nothing and is killed by the suite)
65
+ if (!metadata.suffix)
66
+ return;
67
+ if (this.metadataPerExt.has(metadata.suffix)) {
68
+ this.unsafeExtensions.add(metadata.suffix);
69
+ return;
70
+ }
71
+ this.metadataPerExt.set(metadata.suffix, metadata);
72
+ }
73
+ // content is kept on the clone (not stripped) because it is what tells the
74
+ // rest of the registry — ownsNestedPaths, via declaresNestedContent — that
75
+ // this suffix's type owns every path nested below the shared directory,
76
+ // not just the file itself. Dropping it here would make a component
77
+ // resolved through its content suffix indistinguishable from a type with no
78
+ // nested content, losing the folder segment that tells same-named
79
+ // components under different folders apart.
49
80
  addSharedFolderSuffix(metadata) {
50
81
  if (!metadata.content)
51
82
  return;
52
- const { content: _content, ...metadataWithoutContent } = metadata;
53
83
  for (const sharedFolderMetadataDef of metadata.content) {
54
- this.addSuffix({
55
- ...metadataWithoutContent,
84
+ this.registerSuffix({
85
+ ...metadata,
56
86
  suffix: sharedFolderMetadataDef.suffix,
57
87
  });
58
88
  }
@@ -73,19 +103,23 @@ export class MetadataRepositoryImpl {
73
103
  return !!this.get(path);
74
104
  }
75
105
  get(path) {
106
+ return this.resolve(asFilePath(path));
107
+ }
108
+ // Split out of get() so the key rule, which already holds the stripped
109
+ // path, resolves it without stripping twice; every other caller goes
110
+ // through get() and its single strip.
111
+ resolve(filePath) {
76
112
  // Stryker disable next-line ConditionalExpression -- equivalent: cache short-circuit; flipping to false re-runs the search chain which is deterministic in path, so the result is identical
77
- if (this.pathCache.has(path))
78
- return this.pathCache.get(path);
79
- const parts = path.split(PATH_SEP);
80
- const result = this.searchByExtension(parts) ??
81
- this.searchByDirectory(parts) ??
82
- this.searchByXmlName(path);
83
- this.pathCache.set(path, result);
113
+ if (this.pathCache.has(filePath))
114
+ return this.pathCache.get(filePath);
115
+ const parts = filePath.split(PATH_SEP);
116
+ const result = this.searchByExtension(parts) ?? this.searchByDirectory(parts);
117
+ this.pathCache.set(filePath, result);
84
118
  return result;
85
119
  }
86
120
  searchByExtension(parts) {
87
121
  const extension = parse(parts[parts.length - 1].replace(METAFILE_SUFFIX, '')).ext.replace(DOT, '');
88
- if (MetadataRepositoryImpl.UNSAFE_EXTENSION.has(extension)) {
122
+ if (this.unsafeExtensions.has(extension)) {
89
123
  return;
90
124
  }
91
125
  return this.metadataPerExt.get(extension);
@@ -113,11 +147,16 @@ export class MetadataRepositoryImpl {
113
147
  // directories.
114
148
  ownsNestedPaths(metadata) {
115
149
  return (metadata.inFolder ||
116
- // Stryker disable next-line ConditionalExpression -- equivalent: Set.has(undefined) is already false, so the `!== undefined` operand changes no runtime outcome; it exists only to narrow string|undefined → string for the Set<string>.has call under strict mode
117
- (metadata.adapter !== undefined &&
118
- CONTENT_CONTAINER_ADAPTERS.has(metadata.adapter)) ||
150
+ this.isContentContainer(metadata) ||
119
151
  this.declaresNestedContent(metadata));
120
152
  }
153
+ // Extracted so componentScopedName can pick a container's nesting depth
154
+ // from its adapter without re-deriving what counts as a container.
155
+ isContentContainer(metadata) {
156
+ const adapter = metadata.adapter;
157
+ // Stryker disable next-line ConditionalExpression -- equivalent: Set.has(undefined) is already false, so the `!== undefined` operand changes no runtime outcome; it exists only to narrow string|undefined → string for the Set<string>.has call under strict mode
158
+ return adapter !== undefined && CONTENT_CONTAINER_ADAPTERS.has(adapter);
159
+ }
121
160
  // A type declares nested content when its `content[]` lists suffix-keyed
122
161
  // sub-types sharing one directory (Wave/Bot/Discovery/Moderation): the
123
162
  // segments below that directory are then user-named content, not metadata
@@ -134,33 +173,122 @@ export class MetadataRepositoryImpl {
134
173
  declaresNestedContent(metadata) {
135
174
  return (metadata.content?.length ?? 0) > 0;
136
175
  }
137
- searchByXmlName(xmlName) {
138
- return this.metadataPerXmlName.get(xmlName);
139
- }
140
176
  getByXmlName(xmlName) {
141
177
  return this.metadataPerXmlName.get(xmlName);
142
178
  }
143
179
  getFullyQualifiedName(path) {
144
- let fullyQualifiedName = parse(path).base;
145
- const type = this.get(path);
146
- if (type && MetadataRepositoryImpl.COMPOSED_TYPES.has(type.xmlName)) {
147
- const parentType = path
148
- .split(PATH_SEP)
149
- .find(part => this.metadataPerDir.has(part));
150
- fullyQualifiedName = path
151
- .slice(path.indexOf(parentType))
152
- .replace(PATH_SEP_GLOBAL, '');
180
+ const filePath = asFilePath(path);
181
+ const type = this.resolve(filePath);
182
+ if (!type)
183
+ return parse(filePath).base;
184
+ if (MetadataRepositoryImpl.HOLDER_SCOPED_COMPOSED_TYPES.has(type.xmlName))
185
+ return this.holderScopedName(filePath, type);
186
+ if (MetadataRepositoryImpl.COMPOSED_TYPES.has(type.xmlName))
187
+ return this.composedTypeName(filePath);
188
+ if (!this.ownsNestedPaths(type))
189
+ return this.plainTypeName(filePath, type);
190
+ return this.componentScopedName(filePath, type);
191
+ }
192
+ // The type directory leads a plain key because a suffix belongs to no
193
+ // single type (`policy`, `settings`, `site`, `rule` are each declared by
194
+ // several) — it is the registry's directory, not the file's own, which is
195
+ // what lets a component moved between package directories key the same.
196
+ // Four live registry entries (AssignmentRule, AutoResponseRule,
197
+ // EscalationRule, ManagedTopic) carry a suffix and no directory: the key
198
+ // must never read `undefined/…`.
199
+ plainTypeName(path, type) {
200
+ const fileName = parse(path).base.replace(META_REGEX, '');
201
+ return type.directoryName ? `${type.directoryName}/${fileName}` : fileName;
202
+ }
203
+ // Anchored segment-wise on the first registry directory in the path: a
204
+ // substring search would also hit an unrelated directory that merely
205
+ // contains that name (`objects_backup/objects/…`) and drag it into the key.
206
+ // With no such segment findIndex answers -1 and slice(-1) keeps only the
207
+ // file name, so the fallback every other arm spells out needs no branch.
208
+ composedTypeName(path) {
209
+ const parts = path.split(PATH_SEP);
210
+ const typeIndex = parts.findIndex(part => this.metadataPerDir.has(part));
211
+ return parts.slice(typeIndex).join('');
212
+ }
213
+ // Depth 1 handles every decomposed spelling a holder can appear under: a
214
+ // single file directly beneath its type directory, or a child file nested
215
+ // several segments down — both collapse to the same one-segment container
216
+ // name, because the holder is one component regardless of how SFDX
217
+ // decomposed it into files.
218
+ holderScopedName(path, type) {
219
+ const parts = path.split(PATH_SEP);
220
+ const typeIndex = parts.lastIndexOf(type.directoryName);
221
+ if (typeIndex === -1)
222
+ return parse(path).base;
223
+ return this.containerName(parts, typeIndex, DEFAULT_CONTAINER_DEPTH);
224
+ }
225
+ // inFolder is checked before the container check because a type can be
226
+ // both inFolder and a content container (Document): adapter-first would
227
+ // truncate every Document to its folder, discarding the varying-extension
228
+ // files that folder owns.
229
+ componentScopedName(path, type) {
230
+ const parts = path.split(PATH_SEP);
231
+ const typeIndex = parts.lastIndexOf(type.directoryName);
232
+ if (typeIndex === -1)
233
+ return parse(path).base;
234
+ if (type.inFolder)
235
+ return this.folderScopedName(parts, typeIndex);
236
+ if (this.isContentContainer(type)) {
237
+ const nestedCount = parts.length - typeIndex - 1;
238
+ const depth = this.containerDepth(type.adapter, nestedCount);
239
+ return this.containerName(parts, typeIndex, depth);
153
240
  }
154
- return fullyQualifiedName;
241
+ return this.nestedContentName(parts, typeIndex, type);
242
+ }
243
+ // The `-meta.xml` companion sits outside the extension
244
+ // (`logo.png-meta.xml`), so it must fall away before the extension strip
245
+ // runs — the reverse order would leave it attached and never removed.
246
+ // The trailing `Folder` strip mirrors the folder handler's own name
247
+ // derivation, so the key cannot disagree with the descriptor for a name
248
+ // that happens to end in Folder.
249
+ folderScopedName(parts, typeIndex) {
250
+ return withoutMetaSuffix(parts.slice(typeIndex).join(PATH_SEP))
251
+ .replace(INFOLDER_SUFFIX_REGEX, '')
252
+ .replace(EXTENSION_SUFFIX_REGEX, '');
253
+ }
254
+ // A flat nested-content directory tells its families apart by extension
255
+ // (`wave/A.wdash` vs `wave/A.wapp`), so the extension is kept and any
256
+ // sub-directory is discarded, matching the descriptor. A
257
+ // FOLDER_SCOPED_TYPES member (VirtualBot) is instead named
258
+ // `<bot>.<version>` and needs every segment from the type directory down.
259
+ nestedContentName(parts, typeIndex, type) {
260
+ const tail = MetadataRepositoryImpl.FOLDER_SCOPED_TYPES.has(type.xmlName)
261
+ ? parts.slice(typeIndex)
262
+ : [parts[typeIndex], parts.at(-1)];
263
+ return tail.join(PATH_SEP).replace(META_REGEX, '');
264
+ }
265
+ // A deeper remainder means the container is named by a directory, and a
266
+ // directory name is the component's whole name — stripping it would
267
+ // corrupt one that merely contains a dot (`lwc/foo.bar/…`). Only when the
268
+ // taken segments consume the whole remainder is the name actually a file,
269
+ // and only then are `-meta.xml` and the extension noise to strip away.
270
+ containerName(parts, typeIndex, depth) {
271
+ const nested = parts.slice(typeIndex + 1);
272
+ const name = [parts[typeIndex], ...nested.slice(0, depth)].join(PATH_SEP);
273
+ return depth >= nested.length
274
+ ? withoutMetaSuffix(name).replace(EXTENSION_SUFFIX_REGEX, '')
275
+ : name;
276
+ }
277
+ // Only digitalExperience varies by depth: a page content file lives
278
+ // DIGITAL_EXPERIENCE_CONTENT_DEPTH segments below the bundle directory,
279
+ // while the bundle itself and any shallower/non-canonical layout share the
280
+ // coarser DIGITAL_EXPERIENCE_BUNDLE_DEPTH. Every other content-container
281
+ // adapter names its component by the single segment below its directory.
282
+ containerDepth(adapter, nestedCount) {
283
+ if (adapter !== DIGITAL_EXPERIENCE_ADAPTER)
284
+ return DEFAULT_CONTAINER_DEPTH;
285
+ return nestedCount > DIGITAL_EXPERIENCE_CONTENT_DEPTH
286
+ ? DIGITAL_EXPERIENCE_CONTENT_DEPTH
287
+ : DIGITAL_EXPERIENCE_BUNDLE_DEPTH;
155
288
  }
156
289
  values() {
157
290
  return this.metadatas;
158
291
  }
159
- static UNSAFE_EXTENSION = new Set([
160
- CUSTOM_APPLICATION_SUFFIX,
161
- EMAIL_SERVICES_FUNCTION_SUFFIX,
162
- CUSTOM_METADATA_SUFFIX,
163
- ]);
164
292
  static COMPOSED_TYPES = new Set([
165
293
  OBJECT_TYPE,
166
294
  OBJECT_TRANSLATION_TYPE,
@@ -169,6 +297,20 @@ export class MetadataRepositoryImpl {
169
297
  SHARING_RULE_TYPE,
170
298
  ...SUB_OBJECT_TYPES,
171
299
  ]);
300
+ // A subset of COMPOSED_TYPES: PermissionSet and CustomObjectTranslation
301
+ // decompose into per-file children like every other composed type, but
302
+ // (unlike CustomObject/Workflow/SharingRules) their children are not
303
+ // distinct components — every file under one holder is the same
304
+ // PermissionSet or CustomObjectTranslation, so they key on the holder
305
+ // instead of the decomposed path.
306
+ static HOLDER_SCOPED_COMPOSED_TYPES = new Set([
307
+ PERMISSIONSET_TYPE,
308
+ OBJECT_TRANSLATION_TYPE,
309
+ ]);
310
+ // Nested-content families sharing one flat directory are told apart by
311
+ // extension, except VirtualBot: a BotVersion member is `<bot>.<version>`,
312
+ // so its key must keep the bot folder that the extension alone would lose.
313
+ static FOLDER_SCOPED_TYPES = new Set([VIRTUAL_BOT_TYPE]);
172
314
  }
173
315
  __decorate([
174
316
  log